Update Button Text in LiveView

I have a list of multiple choice(“Challenge”) cards each with a “Reveal Answer” button. With phx-click and phx-value-id I know which Challenge id they clicked to reveal the answer for. I do:

  def handle_event("reveal-answer", %{"id" => id}, socket) do
    IO.inspect(id)

    socket = assign(socket, revealed_answer: Integer.parse(id))
    {:noreply, socket}
  end

In my leex:

          <%= if @revealed_answer == challenge.id do %>
          <button class="btn btn-light rounded-full btn-sm"><%= @revealed_answer %></button>
          <% else %>
          <button class="btn btn-light rounded-full btn-sm" phx-click="reveal-answer" phx-value-id="<%= challenge.id %>">Reveal Answer</button>
          <% end %>

How do I get LiveView to update that one div in the DOM? I am not storing revealed_answer in the db. @revealed_answer is set on mount:

  def mount(_params, _session, socket) do
    socket =
      assign(socket,
      query: "",
      code: "",
      code_search: true,
      loading: false,
      challenges: [],
      suggestions: [],
      revealed_answer: 0,
      total_entries: 0
      )

full source:

regards,
Mike

Does anything happen at the moment when you click the challenge button?

Yes when I click Reveal Answer, my “reveal-answer” handle_event callback gets called. It is the correct Challenge.id.

And what happens with the buttons once the socket assigns are updated? Anything? Have you tried adding something like

<p><%= @revealed_answer %></p>

just before the conditional logic in the template? Also, have you confirmed datatypes are compatible? You could replace

<%= if @revealed_answer == challenge.id do %>

with a private function and IO.inspect the two variables coming in
e.g.

<%= if they_are_equal(@revealed_answer, challenge.id) do %>
...

Also, to be on the safe side, add id attributes to all the important DOM elements (grasping at straws here).

On success, Integer.parse/1 returns a tuple. The first element is the number parsed and the second is the (possibly empty) string that is the remaining unparsed part of the string. So “42” becomes {42, ""} and “42 and some” becomes {42, " and some"}.

So revealed_answer is going to equal something like {42, ""} and this will never equal the integer value of challenge.id.

You need to either use String.to_integer/1 if you are confident the input will always be a number and only a number, or else something like:

   case Integer.parse(id) do
      :error -> {:noreply, socket}
      {number, _} -> {:noreply, assign(socket, revealed_answer: number)}
    end
3 Likes

That was it and it works! I got fooled by my debug IO.inspect(id), for some reason I thought that was the output of Integer.parse/1.

Thanks for the help.

Enjoy the video:

https://pixeldrain.com/api/file/DDCzd1v3

final revised code:

2 Likes