Correct way to get LiveView Hook of element from JavaScript (alpine.js or otherwise)

I am wondering what’s the correct way to fetch an instance of Hook that’s related to given DOM node having it at hand in JavaScript (like, in Alpine.js script or a global script initiated outside of Hooks).

I figured out so far this way:

const el = document.querySelectoir(...);
const liveViewRootEl = window.liveSocket.root.el;
const hookInstance = window.liveSocket.getViewByEl(liveViewRootEl).getHook(el);

Is this the way? What I would like to do is to call functions on the LiveView Hook from Alpine.js code without the need to send messages through the element that is quite tiresome.

Hi @hubertlepicki - I believe that code block is using some private APIs, so be aware that the behaviour could change without warning.

Currently there aren’t any helpers for hooks client-side, but I too have wondered about this. Targeting is a very common tool in traditional client-side frameworks, though I wonder what form it might take in LiveView.

Would you be willing/able to share more about your use case?

Yeah, that’s precisely why I decided to ask on the forum.

So I don’t have precise use case just yet, but I am evaluating alpine.js and how people are integrating it with Phoenix LiveView Hooks. The most comprehensive guide I found is here, and it’s full of ugly hacks. For example:

Hooks.Counter = {
  mounted() {
    window.counterHook = this
  },
  decrement() {
    this.pushEvent('decrement', {})
  },
  increment(selector) {
    this.pushEventTo(selector, 'increment', {})
  }
}

The above is how they turn the Hooks into something à la stimulous controllers, which I think is pretty neat. But the way they use global property on the window object is not great, and will quickly fall apart when you are about to render a list of Counters for example.

In the next section they send events from Alpine to Hooks, which is not great either as it goes through window instance and not the element in one way, and they are able to do it better when sending events from Hooks to Alpine.

I believe it would be both cleaner and less error-prone if we could, having a DOM element find instance of Hook that is, well, hooked to it. We can do the opposite quite easily.

The problem with the hacks I am seeing in that tutorial is that you need to do proper cleanup (which is not mentioned) in order to avoid double callbacks being installed, or memory leaks on the client in the browser if you leave unused callbacks set up on the window object, especially if those callbacks reference DOM nodes.

Having a two way integration would basically turn Hooks into Stimulous controllers on steroids.

2 Likes

Any news on this subject?

It should be possible to push messages to any liveview from any client script using something like a data-phx-root-id maybe together with container ID right?

did you look into pushEventTo function ? JavaScript interoperability — Phoenix LiveView v0.18.18

Tnx, yes I have.
pushEventTo is in scope only inside life-cycle callbacks (Hooks).

This means one needs to define a hook before the liveSocket is initialized.
It’s not possible to load some javascript and just push stuff even though DOM access is available.

Unless of course…

  function push(event, payload) {
    let el = document.getElementById("container-element").parentNode;
    let channel = liveSocket.getRootById(el.getAttribute("data-phx-root-id")).channel;
    channel.push(event, payload);
  };