LiveView not updating after calling assign(socket, :name, "value") in handle_event/3

In template:

<%= for entry <- @payment_destinations do %>
   <tr>
    <td>
            <.button
              type="button"
              phx-click="set_default"
              phx-value-payment_destination_id={entry.id}
            >
              Make Default
            </.button>
     </td>
   </tr>
<% end %>

In LiveView - this is called and the update happens just fine. However, LiveView is not updated.

  @impl true
  def handle_event("set_default", %{"payment_destination_id" => payment_destination_id}, socket) do
    Amplify.Context.PaymentDestinations.update_payment_destination(%{
      "account_id" => socket.assigns.account_id,
      "payment_destination_id" => payment_destination_id
    })
    account_id = socket.assigns.account_id
    socket = put_flash(socket, :info, "Default set")
    |> assign(:payment_destinations, get_payment_destinations(account_id))
    {:noreply, socket}
  end

You will need to share more code as there are several things that could be wrong here and it’s impossible to tell from this code alone.

For instance, when you say “the update happens just fine” do you mean that you can confirm in the database or even with a refresh that it’s updated, or do you just mean that the update_payment_destination function is called without raising? If it returns an ok/error tuple, this code is doing no error handling so it would just chug along as a no-op.

More poignantly, the template code you shared doesn’t have any conditional logic in it that would indicate that the current payment destination is the default or not. At the very least I would expect something like <.button :if={not entry.is_default} ...>Make Default</.button>.

So I figured it out by just adding this another variable to the socket:

    socket =
      put_flash(socket, :info, "Default set")
      |> assign(:payment_destinations, get_payment_destinations(account_id))
      |> assign(:account, Amplify.AccountsContext.get_account(account_id)) # this one

However, I don’t necessarily understand this. In the loop I do a comparison between account.payment_destination.id and entry.id to see which one is the default payment_destination and show a message. Even though account hadn’t changed, I needed to pass it for the loop to do the right comparison. Do you know why?

This is the comparison code in the loop:

    <%= if @account.payment_destination != nil and 
           @account.payment_destination.id == entry.id do %>
      <div class="badge">Default</div>
    <% end %>

What was it before? Assuming account.payment_destination is the default account destination then yes, that makes sense because the account that I assume is originally assigned in the mount isn’t automatically updated. Elixir is an immutable language, so there are no references to structs as there are to objects in OO languages. If I say account = get_account(1) then update_account(account) without re-assigning it, account is still going to be the old data. That is if I understand your thinking correctly.

1 Like

Thank you. You’re absolutely right.