Phoenix_live_view: Updating `phx-click` and `phx-value` attributes

Created on 17 Mar 2019  路  6Comments  路  Source: phoenixframework/phoenix_live_view

Expected behavior

When LiveView updates an element's phx-click and phx-value attributes, the values are updated in the DOM, and click event triggers with new values.

Actual behavior

When LiveView updates an element's phx-click and phx-value attributes, the values are updated in the DOM, but the click event triggers as if the the original values were still there.

Example: clicking the first link works as expected - but the second triggers "show-something" rather than "show-something-else".

defmodule AppWeb.ExampleLive do
  use Phoenix.LiveView

  def render(assigns) do
    ~L"""
      <%= cond do %>
        <% @example-> %>
             <%= link to: "#", "phx-click": "show-something", "phx-value": @example.id do %>
                Link
              <% end %>
        <% @another -> %>
              <%= link to: "#", "phx-click": "show-something-else", "phx-value": @another.id do %>
                Link
              <% end %>
        <% true -> %>
          Lobby
      <% end %>
    """
  end

  def mount(%{example: example}, socket) do
    {:ok, assign(socket, example: example, another: nil)}
  end

  def handle_event("show-something", id, socket) do
    example = Example.get_example(id)
    {:noreply, assign(socket, example: example, another: nil)}
  end

  def handle_event("show-something-else", id, socket) do
    another = Another.get_another(id)
    {:noreply, assign(socket, example: nil, another: another)}
  end
end

Most helpful comment

Update: Adding an id attribute to either the changed element or topmost element in new cond is a temporary fix. ID probably triggers some sort of forced refresh?

All 6 comments

I experienced exactly the same issue yesterday.

A possibly related quirk:
1) Add a second link the the second cond above
2) Move from first cond to second cond via LiveView
3) When showing the second cond, the first link triggers the action from the link in the first cond
4) The second link works as expected

def render(assigns) do ~L""" <%= cond do %> <% @example-> %> <%= link to: "#", "phx-click": "show-something", "phx-value": @example.id do %> Link <% end %> <% @another -> %> <%= link to: "#", "phx-click": "unrelated-action" do %> First link <% end %> <%= link to: "#", "phx-click": "show-something-else", "phx-value": @another.id do %> Second link <% end %> <% true -> %> Lobby <% end %> """ end

Update: Adding an id attribute to either the changed element or topmost element in new cond is a temporary fix. ID probably triggers some sort of forced refresh?

It looks like a "click" event listener is bound to the link with a phxEvent="show-something" here:
https://github.com/phoenixframework/phoenix_live_view/blob/d77f787356ee312eb8a8f8427c03da29a6010abe/assets/js/phoenix_live_view.js#L677-L680

Looking at the code, I expected a new "click" event listener with a phxEvent="show-something-else" to be bound via a call to maybeBindAddedNode:
https://github.com/phoenixframework/phoenix_live_view/blob/d77f787356ee312eb8a8f8427c03da29a6010abe/assets/js/phoenix_live_view.js#L752-L760

But in this case, the existing link element is updated, as you can see when this method is called:
https://github.com/phoenixframework/phoenix_live_view/blob/d77f787356ee312eb8a8f8427c03da29a6010abe/assets/js/phoenix_live_view.js#L426

but not:
https://github.com/phoenixframework/phoenix_live_view/blob/d77f787356ee312eb8a8f8427c03da29a6010abe/assets/js/phoenix_live_view.js#L411

And bindClick is never called, and the existing event listener with the old phxEvent remains.

If you add an id attribute, as suggested in the previous comment, the link element is removed and a new one is added, causing this code to run:
https://github.com/phoenixframework/phoenix_live_view/blob/d77f787356ee312eb8a8f8427c03da29a6010abe/assets/js/phoenix_live_view.js#L411-L418

which calls view.maybeBindAddedNode and binds "click" with a phxEvent="show-something-else"

I don't see any code that would add event listeners in the case of updated elements.

This is not a solution, as it leaves behind the old event listeners, but you get the expected behavior by adding:

onElUpdated: function(toEl) {
   view.maybeBindAddedNode(toEl)
},

right before:
https://github.com/phoenixframework/phoenix_live_view/blob/d77f787356ee312eb8a8f8427c03da29a6010abe/assets/js/phoenix_live_view.js#L426

I came across the same issue today.

I tried using the ID as suggested above, but it had a quirk.

Here's the code I'm running in my .leex file

<%= if @current_link && @current_link.id == link.id do %>
  <%= link "Cancel", to: "#", phx_click: "cancel", id: "cancel-link" %>
<% else %>
  <%= link "Edit", to: "#", phx_click: "edit", phx_value: link.id, id: "edit-link" %>
<% end %>

If I click on the edit link, the cancel link shows up, but the edit link will stay on the screen.

I was able to get around it for now by wrapping one of the links in p tags.

<%= if @current_link && @current_link.id == link.id do %>
  <p><%= link "Cancel", to: "#", phx_click: "cancel" %></p>
<% else %>
  <%= link "Edit", to: "#", phx_click: "edit", phx_value: link.id %>
<% end %>

Hope this info helps.

I ran into an issue similar to this that might be related (happy to open a new issue if requested).

Basically, I have a LiveView with a table and some pagination links like so:

<a href="#"
  data-behavior="prevent-default"
  phx-click="show_all"
>Show all?</a>

<table>
  <th>ID</th>
  <th>Title</th>
<%= for user <- @users %>
  <tr>
    <td><%= user.id %></td>
    <td><%= user.title %></td>
  </tr>
<% end %>
  <tr>
</table>

<%= for page <- @pages %>
<a href="#"
  class="paging__link"
  data-behavior="prevent-default"
  phx-click="page"
  phx-value-page="<%= page %>"
><%= page %></a>
<% end %>

In this case, the "show-all" event changes the data of the table, swaps the current page out for a different set of data.

I'm able to reliably recreate the issue in this case. It seems when elements earlier in the DOM are changed the links will randomly work improperly.

It seems like when they get updated a second url event is sent to the LiveView which is overwriting the updated page param, so clicking the "next" link takes you back to the same page.

I tried the id patch mentioned here which didn't work, but changing the tags from a to span did do the trick.

Was this page helpful?
0 / 5 - 0 ratings