For list in LiveView is not updating with new assigns

I have a select multiple tag which lets users quickly click on a customer and toggle their assignment to a location, salesperson, etc.

  <select class="form-control customer-select select--dual-list" multiple>
        <%= for customer <- @unassigned_customers do %>
        <option value="<%= customer(customer, :id) %>"
          phx-value-customer-name="<%= customer(customer, :name) %>"
          phx-click="customer_add<%= is_update?(@update) %>"
          phx-target="<%= @myself %>"
          class="<%= customer(customer, :added) %>"><%= customer(customer, :name) %></option>
        <% end %>
      </select>

  def handle_event("customer_add", %{"customer-name" => name, "value" => id}, socket),
    do: customer_toggle(socket, id, name, :add)

  def handle_event("customer_remove", %{"customer-name" => name, "value" => id}, socket),
    do: customer_toggle(socket, id, name, :remove)

  def handle_event("customer_add-update", %{"customer-name" => name, "value" => id}, socket),
    do: update_customer_toggle(socket, id, name, :add)
  def customer_toggle(
        %{
          assigns: %{
            assigned_customers: assigned,
            unassigned_customers: unassigned
          }
        } = socket,
        id,
        name,
        direction
      ) do
    {assigned, unassigned} =
      case direction do
        :add ->
          assigned = [{name, id, "added"} | assigned]

          unassigned = Enum.filter(unassigned, &(elem(&1, 1) != id))
          {Enum.sort_by(assigned, &elem(&1, 0)), Enum.sort_by(unassigned, &elem(&1, 0))}

        :remove ->
          assigned = Enum.filter(assigned, &(elem(&1, 1) != id))
          unassigned = unassigned ++ [{name, id, "removed"}]

          {Enum.sort_by(assigned, &elem(&1, 0)), Enum.sort_by(unassigned, &elem(&1, 0))}

        _ ->
          {assigned, unassigned}
      end

    socket
    |> assign(:unassigned_customers, unassigned)
    |> assign(:assigned_customers, assigned)
    |> noreply()
  end

The desired effect is that customer_add will will remove the customer from the “unassigned” list and pass them into an “assigned” list.

screencast 2022-08-22 12-21-18

This was working until I upgrade to Phoenix .16+, however now the updated assigns, specifically customers removed from a list are still showing in the list.


Edit: Phoenix upgrade is not the issue… I just switched branches and saw it’s still happening, but not on production. This is helpful because I only have about half a dozen commits between my current branch and production. When I sort this out I’ll post an explanation just for posterity.

screencast 2022-08-22 12-22-16

I’ve tried adding inspect statements inside the for statement to see what it the @unassigned_customers is, and the correct customer is being removed with each click, but the UI isn’t reflecting that. Nothing in the LiveView changed with these updates, and initially nothing changed with the template, although I have played around changing to heex or extracting the option tag into it’s own component- neither of which seemed to make a difference.

PS. Please excuse the Quickbooks developer account customer names…

Can we see your def handle_event part too?

I was hoping not too because it’s a little verbose/long but I’ll edit my question to include it…

Still having issues with this, I refactored to an HEEX template and moved the select logic into a LiveComponent, mostly to test this in a bit more isolation… Still no luck:

 <.live_component module={DualSelect} id="dual_select" assigned_customers={@assigned_customers} unassigned_customers={@unassigned_customers} />

Updated component:

def render(assigns) do
    ~H"""
    <div class="row">
      <div class="col-md-6">
        <div class="form-group">
          <label>Unassigned customers</label>
          <%= select_box(@unassigned_customers, :add) %>
        </div>
      </div>
      <div class="col-md-6">
        <div class="form-group">
          <label>Assigned Customers</label>
          <%= select_box(@assigned_customers, :remove) %>
        </div>
      </div>
    </div>
    """
  end

  defp select_box(customers, direction) do
    assigns = %{customers: customers, direction: direction}

    ~H"""
    <select class="form-control customer-select select--dual-list" multiple>
      <%= for customer <- @customers do %>
        <option value={customer(customer, :id)} phx-value-customer-name={customer(customer, :name)} phx-click={direction(direction)} class={customer(customer, :added)}>
           <%= customer(customer, :name) %>
        </option>
      <% end %>
    </select>
    """
  end

I’ve added inspects to the assigns passed to render/1 and with each toggle the assigns for both @unassigned_customers and @assigned_customers are updating, but only one side is rendering the change.

PS. Sorry I would have edited my OG question but I think I posted to far in the past to still edit…


Update… It seems like I can get the list to update if it’s not specifically the list I’m clicking. I duplicated one of the lists and if I remove items from the duplicate list the other lists will update accordingly and vice versa. So it seems like when an item gets selected it won’t be removed in the UI by the LiveView update?
screencast 2022-08-31 13-20-08

I decided it must be some weirdness with multiselects so I changed the select box to a div and filled it with buttons instead of options and it’s working now :person_shrugging: