Shadowing assigns in LiveView with AlpineJS, not working?

I’m having trouble getting Alpinejs to “shadow” and “track” assigns in my live component. For instance using an example from Integrating Phoenix LiveView with JavaScript and AlpineJS I am not getting the expected behavior. My “assigns count” shows up and increments/decrements as it should, but I get nothing for the “alpine count”. Edit to add: I’m actually noticing now that the “alpine count” is actually rendering correctly for a split second on load, but disappears after. phx-update="ignore" does make it stay but I loose the ability to increment/decrement the count.

 <div id="counter"
      x-data="{count: <%= @count %>}">
  <h1>The assigns count is: <span><%= @count %></span></h1>
  <h1>The alpine count is: <span x-text="count"></span></h1>
  <button phx-click="decrement" phx-target=<%= @myself %> %>> Decrement </button>
  <button phx-click="increment" phx-target=<%= @myself %>> Increment </button>
</div>

Likewise, I have the below code where myHookOnWindow.myRenderData(data) runs fine and renders the data, but nothing happens with $watch('data', () => console.log('data changed')) when that data changes. The data is JSON in this case because it complains if I use a regular map or list.

<canvas
  id="MyChart"
  phx-hook="myHook"
  x-data="{data: <%= @data %>}"
  x-init="
    myHookOnWindow.myRenderData(data)

    $watch('data', () => console.log('data changed'))
  "
>
</canvas>
1 Like

If you are using Alpine 3, it needs new method to integrate. Checkout Shadowed assigns with AlpineJS v3 and Phoenix LiveView 0.15.

Are you referring to adding:

if (from._x_dataStack) {
     window.Alpine.clone(from, to);
     window.Alpine.initTree(to)
}

if so, I currently have:

dom: {
   onBeforeElUpdated(from, to) {
     if (from.__x) {
       window.Alpine.clone(from.__x, to)
     }
   }
 },

Would the new code replace my current if or be an additional if in that method. Also is _x_dataStack supposed to have two underscores instead of one on both sides of the x?

So, I added the if from my above comment to my onBeforeElUpdated method and that did not work for me. I did try setting the x-data property via x-init with the assign I wanted to shadow and that seemed to work. Man, I wish the docs for Alpine stayed up to date and covered some of this fundamental stuff.

<div
 x-data="{data: ''}"
 x-init="
   data = <%= @data %>
   $watch('data', () => console.log('data changed'))
 "
>
</div>

If it’s any help, I was playing around with AlpineJS and Liveview for the first time today and had no issues with shadowing. I was actually going through the same article as you :thinking: But I did have to google some things for AlpineJS 3.

Here’s what I have in my app.js

import Alpine from "alpinejs"

// other JS code

window.Alpine = Alpine
Alpine.start()

let liveSocket = new LiveSocket("/live", Socket, {
  params: { _csrf_token: csrfToken },
  dom: {
    onBeforeElUpdated(from, to) {
      if (from._x_dataStack) {
        window.Alpine.clone(from, to)
      }
    },
  },
})

Hopefully it will help?

Are there any errors in the JS console by the way?

1 Like

No errors in the console. I tried your if in the dom method onBeforeElUpdated, but can still only shadow assigns if I assign them to x-data via x-init like mentioned above. I guess that’ll have to work for now. Not sure if it matters but I’m on elixir 1.10.4-otp-22, erlang 22.3.4, {:phoenix_live_view, "~> 0.15"}, {:phoenix, "~> 1.5.0", override: true}, {:phoenix_html, "~> 2.10"}.