Nice solution
! Explicit server-pushed events give much more granular control than phx-mounted and phx-remove.
I think this is very correct.
Sometimes I choose to use a global event listener, so that I have a single listener that applies the action to an element given a selector, instead of multiple instances of the hook. Note that when server sent events bubble up to the window they get the phx: prefix (JavaScript interoperability — Phoenix LiveView v1.1.16).
For example, in app.js:
window.addEventListener("phx:fade_in", event => {
const el = document.querySelector(event.detail.selector);
if (!el) return;
liveSocket.js().transition(el, ["ease-out duration-200", "opacity-0 scale-95", "opacity-100 scale-100"]);
});
On the server side:
socket
|> push_event("fade_in", %{"selector" => "#urls-#{url.id}"})
A more generic listener could even exec any set of JS commands stored in an attribute (or sent along the event payload):
window.addEventListener("phx:exec", event => {
const el = document.querySelector(event.detail.selector);
if (!el) return;
liveSocket.execJS(el, event.detail.js || el.getAttribute(event.detail.attr));
});
On the server side:
<div
id={@id}
data-phx-show={JS.transition({"ease-out duration-200", "opacity-0 scale-95", "opacity-100 scale-100"})}
>...</div>
socket
|> push_event("exec", %{
"selector" => "#urls-#{url.id}",
"attr" => "data-phx-show"
})
Or (see Make `JS.t()` a public data structure, or json serializable):
socket
|> push_event("exec", %{
"selector" => "#urls-#{url.id}",
"js" =>
JS.transition({"ease-out duration-200", "opacity-0 scale-95", "opacity-100 scale-100"}).ops
|> JSON.encode!()
})




















