Unable to meet condition because value not being passed to param from socket

Hello! I’ve been working with phoenix and liveview and am working through the programming liveview book. At the end of the first chapter you expand on a guessing game concept that you touch on and are challenged to make it function properly, as the chapter only has the user losing. I’ve got the user’s guess coming across the line fine, but not the value set in the mount and I’m not sure where I’m going wrong. I can see the value itself in the socket

My LiveView looks like this:

defmodule PracticeWeb.GuessLive do
  use PracticeWeb, :live_view

  @impl Phoenix.LiveView
  def mount(_params, _session, socket) do
    {:ok,
      socket
      |> assign(
          score: 0,
          message: "Can you guess the right number?")
      |> assign_new(:value, fn -> Enum.random(1..10) end)
      |> IO.inspect(label: :mount_values)
    }
  end

  @spec render(any) :: Phoenix.LiveView.Rendered.t()
  @impl Phoenix.LiveView
  def render(assigns) do
    ~H"""
    <.section
      title="Guess the right number">
      <h2><%= @message %></h2>
      <%= @value %>
      <div class="w-full inline-grid grid-cols-1 sm:grid-cols-3 gap-4 my-4">
        <%= for n <- 1..10 do %>
        <.button class="btn-sm w-full sm:w-48" phx-click="users_guess" phx-value-number={n}>
          <%= n %>
        </.button>
        <% end %>
      </div>
      <p>Your score: <span class="bold"><%= @score %></span></p>
    </.section>
    """
  end

  @impl Phoenix.LiveView
  def handle_event(
    "users_guess",
    %{"number" => guess},
    socket)
    do

    case guess == @value do
      true ->
        message = "You guessed #{guess}. Correct! Can you guess correctly again?"
        score = socket.assigns.score + 1

      {:noreply,
        socket
        |> assign( message: message, score: score)
        |> assign_new(:value, fn -> Enum.random(1..10) end)}

      false ->
        message = "You guessed #{guess}. Wrong! Guess again."
        score = socket.assigns.score - 1

        {:noreply,
        socket
        |> assign( message: message, score: score)
        |> IO.inspect(label: :socket_wrong)}
    end

  end
end

And the output is looking like this:

[debug] Processing with PracticeWeb.GuessLive.index/2
  Parameters: %{}
  Pipelines: [:browser]
mount_values: #Phoenix.LiveView.Socket<
  id: "phx-F1scb7yHrlYhXhyC",
  endpoint: PracticeWeb.Endpoint,
  view: PracticeWeb.GuessLive,
  parent_pid: nil,
  root_pid: nil,
  router: PracticeWeb.Router,
  assigns: %{
    __changed__: %{message: true, score: true, value: true},
    flash: %{},
    live_action: :index,
    message: "Can you guess the right number?",
    score: 0,
    value: 1
  },
  transport_pid: nil,
  ...
>
[info] Sent 200 in 4ms
[info] CONNECTED TO Phoenix.LiveView.Socket in 31µs
  Transport: :websocket
  Serializer: Phoenix.Socket.V2.JSONSerializer
  Parameters: %{"_csrf_token" => "bT4xPFkZCDsAcyQ9dS89IBAyAxUuOgYR8xbQka8uOGhUBiKjgpnPaxrF", "_live_referer" => "undefined", "_mounts" => "0", "_track_static" => %{"0" => "http://localhost:4000/assets/app.css", "1" => "http://localhost:4000/assets/app.js"}, "vsn" => "2.0.0"}
[debug] MOUNT PracticeWeb.GuessLive
  Parameters: %{}
  Session: %{"_csrf_token" => "UFSm2x0NO4Lh7FvJwBmEOBtW"}
mount_values: #Phoenix.LiveView.Socket<
  id: "phx-F1scb7yHrlYhXhyC",
  endpoint: PracticeWeb.Endpoint,
  view: PracticeWeb.GuessLive,
  parent_pid: nil,
  root_pid: #PID<0.2894.0>,
  router: PracticeWeb.Router,
  assigns: %{
    __changed__: %{message: true, score: true, value: true},
    flash: %{},
    live_action: :index,
    message: "Can you guess the right number?",
    score: 0,
    value: 3
  },
  transport_pid: #PID<0.2888.0>,
  ...
>
[debug] Replied in 671µs
[debug] HANDLE EVENT
  View: PracticeWeb.GuessLive
  Event: "users_guess"
  Parameters: %{"number" => "6", "value" => ""}
socket_wrong: #Phoenix.LiveView.Socket<
  id: "phx-F1scb4bEH4KG5xtC",
  endpoint: PracticeWeb.Endpoint,
  view: PracticeWeb.GuessLive,
  parent_pid: nil,
  root_pid: #PID<0.2873.0>,
  router: PracticeWeb.Router,
  assigns: %{
    __changed__: %{message: true, score: true},
    flash: %{},
    live_action: :index,
    message: "You guessed 6. Wrong! Guess again.",
    score: -1,
    value: 6
  },
  transport_pid: #PID<0.2867.0>,
  ...
>
[debug] Replied in 1ms

Thanks for any help or guidance!

Hey @jmarkle52 welcome!

This is your issue.

@value syntax inside of a template pulls out assigns, but the handle_event function isn’t part of a template, it’s regular elixir code. This means that @value is a module attribute, not a moment where you are accessing assigns. You probably want:

case guess == socket.assigns.value do

The only other gotcha I can think of is you’ll want to sanity check whether guess is coming in as an integer or as a string. If it’s a string, then you won’t get the match that you expect.

1 Like

Hey @benwilson512 thanks for the suggestion. I actually have tried that as well, but the result is exactly the same which was something that really had confused me. Especially because I can inspect the socket and see the value of value but only outside of the params.

[debug] MOUNT PracticeWeb.GuessLive
Parameters: %{}
Session: %{“_csrf_token” => “UFSm2x0NO4Lh7FvJwBmEOBtW”}
mount_values: #Phoenix.LiveView.Socket<
id: “phx-F1sdYCZc4wxToiRB”,
endpoint: PracticeWeb.Endpoint,
view: PracticeWeb.GuessLive,
parent_pid: nil,
root_pid: #PID<0.2955.0>,
router: PracticeWeb.Router,
assigns: %{
changed: %{message: true, score: true, value: true},
flash: %{},
live_action: :index,
message: “Can you guess the right number?”,
score: 0,
value: 3
},
transport_pid: #PID<0.2949.0>,

[debug] Replied in 659µs
[debug] HANDLE EVENT
View: PracticeWeb.GuessLive
Event: “users_guess”
Parameters: %{“number” => “3”, “value” => “”}
socket_wrong: #Phoenix.LiveView.Socket<
id: “phx-F1sdYCZc4wxToiRB”,
endpoint: PracticeWeb.Endpoint,
view: PracticeWeb.GuessLive,
parent_pid: nil,
root_pid: #PID<0.2955.0>,
router: PracticeWeb.Router,
assigns: %{
changed: %{message: true, score: true},
flash: %{},
live_action: :index,
message: “You guessed 3. Wrong! Guess again.”,
score: -1,
value: 3
},
transport_pid: #PID<0.2949.0>,

[debug] Replied in 1ms

Hey @jmarkle52 can you show me exactly the code you tried?

You aren’t wrong, the value is on the socket. The issue is that @value is just flat wrong inside of your function. It will not work. Can you try:

guess |> IO.inspect(label: :guess)
socket.assigns.value |> IO.inspect(label: :assigns_value)
case guess == socket.assigns.value do

Sure! Here’s the event handler, and I’ll repost the liveview in context of that below it

@impl Phoenix.LiveView
  def handle_event(
    "users_guess",
    %{"number" => guess},
    socket)
    do

    case guess == socket.assigns.value do
      true ->
        message = "You guessed #{guess}. Correct! Can you guess correctly again?"
        score = socket.assigns.score + 1

      {:noreply,
        socket
        |> assign( message: message, score: score)
        |> assign_new(:value, fn -> Enum.random(1..10) end)}

      false ->
        message = "You guessed #{guess}. Wrong! Guess again."
        score = socket.assigns.score - 1

        {:noreply,
        socket
        |> assign( message: message, score: score)
        |> IO.inspect(label: :socket_wrong)}
    end
defmodule PracticeWeb.GuessLive do
  use PracticeWeb, :live_view

  @impl Phoenix.LiveView
  def mount(_params, _session, socket) do
    {:ok,
      socket
      |> assign(
          score: 0,
          message: "Can you guess the right number?")
      |> assign_new(:value, fn -> Enum.random(1..10) end)
      |> IO.inspect(label: :mount_values)
    }
  end

  @spec render(any) :: Phoenix.LiveView.Rendered.t()
  @impl Phoenix.LiveView
  def render(assigns) do
    ~H"""
    <.section
      title="Guess the right number">
      <h2><%= @message %></h2>
      <%= @value %>
      <div class="w-full inline-grid grid-cols-1 sm:grid-cols-3 gap-4 my-4">
        <%= for n <- 1..10 do %>
        <.button class="btn-sm w-full sm:w-48" phx-click="users_guess" phx-value-number={n}>
          <%= n %>
        </.button>
        <% end %>
      </div>
      <p>Your score: <span class="bold"><%= @score %></span></p>
    </.section>
    """
  end

  @impl Phoenix.LiveView
  def handle_event(
    "users_guess",
    %{"number" => guess},
    socket)
    do

    case guess == socket.assigns.value do
      true ->
        message = "You guessed #{guess}. Correct! Can you guess correctly again?"
        score = socket.assigns.score + 1

      {:noreply,
        socket
        |> assign( message: message, score: score)
        |> assign_new(:value, fn -> Enum.random(1..10) end)}

      false ->
        message = "You guessed #{guess}. Wrong! Guess again."
        score = socket.assigns.score - 1

        {:noreply,
        socket
        |> assign( message: message, score: score)
        |> IO.inspect(label: :socket_wrong)}
    end

  end
end

Can you include the IO.inspect values I put in along with the logged output?

Tangentially, I was able to add formatting to your post via ``` around each code block.

Thanks, sorry about that, I was trying to find an edit button for my post but the color contrast is so low between the icons and the bg color that I missed it.

Here’s the return after adding those IO.inspect values above the case statement

[debug] MOUNT PracticeWeb.GuessLive
  Parameters: %{}
  Session: %{"_csrf_token" => "UFSm2x0NO4Lh7FvJwBmEOBtW"}
mount_values: #Phoenix.LiveView.Socket<
  id: "phx-F1selcKrKAt61x6C",
  endpoint: PracticeWeb.Endpoint,
  view: PracticeWeb.GuessLive,
  parent_pid: nil,
  root_pid: #PID<0.3024.0>,
  router: PracticeWeb.Router,
  assigns: %{
    __changed__: %{message: true, score: true, value: true},
    flash: %{},
    live_action: :index,
    message: "Can you guess the right number?",
    score: 0,
    value: 3
  },
  transport_pid: #PID<0.3018.0>,
  ...
>
[debug] Replied in 657µs
[debug] HANDLE EVENT
  View: PracticeWeb.GuessLive
  Event: "users_guess"
  Parameters: %{"number" => "3", "value" => ""}
guess: "3"
assigns_value: 3
socket_wrong: #Phoenix.LiveView.Socket<
  id: "phx-F1selcKrKAt61x6C",
  endpoint: PracticeWeb.Endpoint,
  view: PracticeWeb.GuessLive,
  parent_pid: nil,
  root_pid: #PID<0.3024.0>,
  router: PracticeWeb.Router,
  assigns: %{
    __changed__: %{message: true, score: true},
    flash: %{},
    live_action: :index,
    message: "You guessed 3. Wrong! Guess again.",
    score: -1,
    value: 3
  },
  transport_pid: #PID<0.3018.0>,
  ...
>
[debug] Replied in 2ms

Here’s the event handler in context

def handle_event(
    "users_guess",
    %{"number" => guess},
    socket)
    do

    guess |> IO.inspect(label: :guess)
    socket.assigns.value |> IO.inspect(label: :assigns_value)
    case guess == socket.assigns.value do
      true ->
        message = "You guessed #{guess}. Correct! Can you guess correctly again?"
        score = socket.assigns.score + 1

      {:noreply,
        socket
        |> assign( message: message, score: score)
        |> assign_new(:value, fn -> Enum.random(1..10) end)}

      false ->
        message = "You guessed #{guess}. Wrong! Guess again."
        score = socket.assigns.score - 1

        {:noreply,
        socket
        |> assign( message: message, score: score)
        |> IO.inspect(label: :socket_wrong)}
    end

  end

Right so this is what I was saying about it coming through as a string. Elixir does not implicitly coerce text into numbers. guess is a string, and your assigns_value is an integer. You need either convert the guess into a number, or the assigns value into a string eg:

case guess == to_string(socket.assigns.value) do
1 Like

Ok, I see what you mean now. Sorry, end of the day and my brain is mush. Thanks so much!

1 Like