phoenixframework / phoenix_live_view

Rich, real-time user experiences with server-rendered HTML
https://hex.pm/packages/phoenix_live_view
MIT License
6.14k stars 919 forks source link

Lacking a way to pass `phx-value-` from element using `JS.exec` #3316

Closed DaTrader closed 3 months ago

DaTrader commented 3 months ago

The use case is having a stream container with LiveComponent items whose JS on, say phx-click depends on an assign in the stream container LiveComponent. Being highly impractical to update all of the stream items only to propagate an update of such assign, it's convenient to have the items use a JS.exec that executes a centralized JS command e.g. a JS.push defined in a container LiveView/LiveComponent element.

However, such centralized JS.push includes in its payload only the value parameters specified in its own element (the foo param in this case), which would otherwise be sufficient, but in this case it would be very convenient to be able to combine (merge) the value parameters of the item executing the centralized JS command as well (the 'bar` param in this case).

Ex:

Stream container LiveView or LiveComponent:

<div 
  id={@id} phx-update="stream"
  data-centralized-push={JS.push( "do-something")} phx-value-foo={@a_volatile_assign}
>
  <.live_component
    ..
    container_id={@id}
  />
</div>

Stream item LiveComponent:

<div id={@id} ..>
  <button 
    type="button" 
    phx-click={JS.exec( "data-centralized-push", to: @container_id)} 
    phx-value-bar={@item_specific_assign}
  >
    Click me
  </button>
</div>

Thanks!

chrismccord commented 3 months ago

Your best bet in this case is use a hook which listeners for a dispatch from the stream item, like `phx-click={JS.dispatch("my-push,", detail: %{bar: @item_specific_assign})}, and within the and pushEvent yourself you merge the e.detail with whatever top level data you have. You could also have a hook per stream item that does the same thing, but reads and merges some parent info and skip the JS.dispatch. Propagating values would be doable, but it's highly specific to push, and JS.exec is about executing any JS chain, so I am not convinced it's what we want to do. Thanks!

DaTrader commented 3 months ago

Yes, doing it with a hook already and you're correct that what I suggest would be strongly related to JS.push.