# Arbitrary JavaScript on LiveView update

**URL:** <https://forum.elixirforum.com/t/arbitrary-javascript-on-liveview-update/27267>\
**Category:** Questions\
**Tags:** phoenix, liveview\
**Created:** [December 2, 2019, 2:44pm UTC](https://forum.elixirforum.com/t/arbitrary-javascript-on-liveview-update/27267 "2019-12-02T14:44:39Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![ndarilek](https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/n/73ab20/32.png) [@ndarilek](https://forum.elixirforum.com/u/ndarilek)\
**Post date:** [December 2, 2019, 2:44pm UTC](https://forum.elixirforum.com/t/arbitrary-javascript-on-liveview-update/27267/1 "2019-12-02T14:44:39Z")

</div>

I’m about to use LiveView for the first time. I think I get how it works when updating the DOM, hiding/changing controls, etc. but is it possible to run arbitrary JavaScript when a new message is received?

We’re building a document conversion service. When the page loads, I set the title to “Loading…” Eventually, our converter returns an actual document title, at which point I want to run `document.title = title` or something equivalent to include the actual document’s title in the page. Titles are generated in the layout’s view, so aren’t in the LiveView itself.

Maybe there’s a better way to set the title, but is it possible to run other client-side code when certain messages are received? We have some complex client-side state that doesn’t always translate to DOM updates–libraries that need to be called on the client whenever new content is received, for instance. I think hooks may help here, but I’m not clear how, nor am I clear how to use specific hooks on specific pages rather than globally.

Thanks.

---

<div class="post-metadata">

**Author:** ![mindok](https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/mindok/32/13538_2.png) [@mindok](https://forum.elixirforum.com/u/mindok)\
**Post date:** [December 2, 2019, 11:47pm UTC](https://forum.elixirforum.com/t/arbitrary-javascript-on-liveview-update/27267/2 "2019-12-02T23:47:20Z")

</div>

Hi @ndarilek,

Hooks are the way to go, and they can (and actually have to be) linked to specific elements in your LiveView.

The docs cover this:

> **[Phoenix.LiveView — Phoenix LiveView v1.2.5](https://phoenix-live-view.hexdocs.pm/Phoenix.LiveView.html)**

But you are probably better watching the video of when it was announced to get an overview:

[![](https://img.youtube.com/vi/txk4WAlabvI/maxresdefault.jpg "ElixirConf 2019 - Friday Evening Keynote - Chris McCord") ](https://www.youtube.com/watch?v=txk4WAlabvI&t=1100s)

Edit: I haven’t figured out a way to nicely manage different hooks for different pages/views yet (I’m not really that up to speed with JS - that’s why I’m here!!) - I’m sure someone else can help with that.

---

<div class="post-metadata">

**Author:** ![snewcomer](https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/snewcomer/32/9343_2.png) [@snewcomer](https://forum.elixirforum.com/u/snewcomer)\
**Post date:** [December 3, 2019, 2:51am UTC](https://forum.elixirforum.com/t/arbitrary-javascript-on-liveview-update/27267/3 "2019-12-03T02:51:59Z")

</div>

Currently, hooks only respond to `mounted` (when element under view is added and LV has finished mounting) or `updated` (when the DOM has been updated). It seems like you have neither and need state available client side.

One thing I would like to explore is a meta tags solution for LV, especially as it relates to `live_link`. Effectively, we need to propagate changes server side to the meta tags client side and I don’t think we have a solution yet.

---

<div class="post-metadata">

**Author:** ![mindok](https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/mindok/32/13538_2.png) [@mindok](https://forum.elixirforum.com/u/mindok)\
**Post date:** [December 3, 2019, 5:41am UTC](https://forum.elixirforum.com/t/arbitrary-javascript-on-liveview-update/27267/4 "2019-12-03T05:41:43Z")

</div>

Hi @ndarilek,  
Thinking about it some more, you could create a hidden element with a `phx-hook` and push the messages to that, e.g.

Template (leex) - spot to push messages content, plus JS hook

```elixir
  <div hidden 
    id="msg-thing" 
    data-messages="<%= Jason.encode!(@messages) %>" 
    phx-hook="MessageChange" />

```

Live controller

```elixir
  # Assumes messages published to a PubSub topic the LiveView subscribes to
  # Also assumes an empty list of messages is assigned to the socket on mount...
  def handle_info({:msg_added, %Message{}=msg}, socket) do
    messages = [msg | socket.assigns.messages]
    {:noreply, assign(socket, messages: messages)}
  end

```

JS

```javascript
Hooks.MessageChange = {
  mount() { this.handleMessages(); },
  updated() { this.handleMessages(); },
  
  handleMessages() {
    var messages = JSON.parse(this.el.dataset.messages);
    // Do what you need to with messages
  }
}

```

---

<div class="post-metadata">

**Author:** ![evomedia247](https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/evomedia247/32/17475_2.png) [@evomedia247](https://forum.elixirforum.com/u/evomedia247)\
**Post date:** [December 3, 2019, 7:14am UTC](https://forum.elixirforum.com/t/arbitrary-javascript-on-liveview-update/27267/5 "2019-12-03T07:14:57Z")

</div>

I mentioned in another post about knockout js it’s super easy to use an a way to sprinkle extra ui js onto server rendered templates. I often handle default messages with server side code with it. .

You bind your live view data to a knockout model, then use that to handle things like the foreach for data display. I had created a git repo showing knockout rendering when the websocket updates.

[https://github.com/evomedia247/Phoenix-and-knockout-example](https://github.com/evomedia247/Phoenix-and-knockout-example)

[https://forum.elixirforum.com/t/using-knockout-js-to-bind-phoenix-websockets-to-the-ui/27145/3](https://forum.elixirforum.com/t/using-knockout-js-to-bind-phoenix-websockets-to-the-ui/27145/3)

---

<div class="post-metadata">

**Author:** ![evomedia247](https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/evomedia247/32/17475_2.png) [@evomedia247](https://forum.elixirforum.com/u/evomedia247)\
**Post date:** [December 3, 2019, 7:28am UTC](https://forum.elixirforum.com/t/arbitrary-javascript-on-liveview-update/27267/6 "2019-12-03T07:28:45Z")

</div>

In generally i only need loading text etc on big initial data loads,

I would say that in almost any templating language, you can have 2 elements, one shown if data is null… showing loading, and another to render the data if present.

Or if a div has a data bind on it, if you add default text inside that element, that will be displayed if no data is present to replace it.

In live view the websocket traffic is generally so fast and the ui update happens instantly on receiving that data, showing loading is sort of pointless after page load. I see more too adding things like ‘is typing’

---

<div class="post-metadata">

**Author:** ![ndarilek](https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/n/73ab20/32.png) [@ndarilek](https://forum.elixirforum.com/u/ndarilek)\
**Post date:** [December 12, 2019, 10:54pm UTC](https://forum.elixirforum.com/t/arbitrary-javascript-on-liveview-update/27267/7 "2019-12-12T22:54:39Z")

</div>

Thanks folks, sorry it’s taken a while to come back around to this. I  
finally had some time to dive into actually implementing this last week.

I’ve gotten pretty far with this, but I just want to check my  
understanding. So essentially, when I initially load my document, I have  
`<title>Loading...</title>` in my HTML document. Eventually my GRPC  
service processes my PDF and returns a document title, at which point I  
want to set `<title>My Document Title</title>` on my HTML document. But  
I can’t wrap my entire page in a hook.

So I’m guessing the answer here is to put my document title in an  
element under a `phx-hook` binding, something like:

```elixir

<div phx-hook="DocumentShow">

   <h1 id="document_title"><%= @document.title %></h1>

</div>

```

Then my `updated` hook does something like `document.title = el.querySelector("#document_title").innerHtml`? All off the top of my  
head, so I probably got capitalization wrong somewhere.

So in other words, if I want data from the server on which to run  
arbitrary JavaScript, I have to package it in the DOM elements somehow,  
then extract it in my hook. Is that accurate? If so, then thanks for  
helping me figure that out. 🙂 I’d rather not package in a client-side  
framework just yet, since so far most of my logic can run server-side,  
and it’s only really this one bit of functionality outside of the  
rendered template that I need this for. If I need more complexity, I’ll  
definitely reach for something heavier.

---

<div class="post-metadata">

**Author:** ![mindok](https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/mindok/32/13538_2.png) [@mindok](https://forum.elixirforum.com/u/mindok)\
**Post date:** [December 12, 2019, 11:20pm UTC](https://forum.elixirforum.com/t/arbitrary-javascript-on-liveview-update/27267/8 "2019-12-12T23:20:36Z")

</div>

Hi @ndarilek,  
Conceptually you are spot on. Package your data into a DOM element that has a JS hook attached, and use it in the hook. Your version may not work as the hook has to be associated with an element that has a socket assign that is changing in order for it to fire, and also note that the hook has direct access to the element it is associated with so you don’t need to look it up with a selector. I’d personally code it along the lines of:

leex

```elixir
<div id="my-id" data-title="<%= @document.title %>" phx-hook="DocumentShow"></div>

```

and in the JS:

```javascript
Hooks.DocumentShow = {
  mounted() {
    this.updateTitle();
  },
  updated() {
     this.updateTitle();
  },
  updateTitle() {
    var title = this.el.dataset.title; // You have access to the element within the hook
    document.title = title;
  }
};

```

---

<div class="post-metadata">

**Author:** ![ndarilek](https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/n/73ab20/32.png) [@ndarilek](https://forum.elixirforum.com/u/ndarilek)\
**Post date:** [December 12, 2019, 11:32pm UTC](https://forum.elixirforum.com/t/arbitrary-javascript-on-liveview-update/27267/9 "2019-12-12T23:32:27Z")

</div>

Nice, thanks, haven’t done much with data attributes so that’s an  
elegant solution.

---

<div class="post-metadata">

**Author:** ![snewcomer](https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/snewcomer/32/9343_2.png) [@snewcomer](https://forum.elixirforum.com/u/snewcomer)\
**Post date:** [December 13, 2019, 6:46am UTC](https://forum.elixirforum.com/t/arbitrary-javascript-on-liveview-update/27267/10 "2019-12-13T06:46:19Z")

</div>

(great idea with the hidden elements!)

I think one important point is that your client side code will only change once one of the following [callbacks](https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html#callbacks) happens server side. It necessarily doesn’t happen “automagically”…although it may see like it ;).

So for you to trigger an `update` to the UI, you need to send a message to the LiveView or LiveComponent on the server and update the `assigns` (as mentioned above). When the websocket message gets to the client, it will diff/update/call relevant hooks to update your UI. Sorry if I’m being repetitive 🙏 if this is something you are already aware of but I thought a few links might help.

LiveView Component [send\_update/2](https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html#send_update/2)  
[example using send\_update/2](https://github.com/snewcomer/live-comment/blob/c74b54b75d82bce2c89ff98ec680af8fce208de7/lib/live_comment_web/live/comment_live/index.ex#L50)

As shown, it is rather tedious and I would love to help make this process more ergonomical in the future to update `<head>` tags. Perhaps time to start experimenting!
