simoncocking

simoncocking

We’re running into an odd problem with AlpineJS and Phoenix LiveView, whereby Alpine reacts to the previous value of an assign when it changes. For example, with the following LEEx (lifted verbatim from Patrick Thompson’s excellent blog post:

<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>
</div>

With Alpine v2, both the assigns count and Alpine count are kept in sync whenever the assign is updated however with Alpine v3, the Alpine counter is always one update behind the assign.

I’ve illustrated the whole problem in this Loom video.

Has anyone else seen (or can reproduce) this issue? The full LiveView is as follows:

defmodule MyApp.AlpineLive do
  use Phoenix.LiveView

  @impl Phoenix.LiveView
  def render(assigns) do
    ~L"""
    <div id="counter" class="m-8" 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 class="button button-primary" phx-click="decrement"> Decrement </button>
      <button class="button button-primary" phx-click="increment"> Increment </button>
    </div>
    """
  end

  @impl Phoenix.LiveView
  def mount(_, _, socket) do
    count = if connected?(socket), do: 5, else: 0
    {:ok, assign(socket, count: count)}
  end

  @impl Phoenix.LiveView
  def handle_event("increment", _, socket) do
    {:noreply, update(socket, :count, &(&1 + 1))}
  end

  def handle_event("decrement", _, socket) do
    {:noreply, update(socket, :count, &(&1 - 1))}
  end
end

FWIW our app.js integrates AlpineJS with the following option on the LiveSocket (which supports both Alpine v2 and v3):

  dom: {
    onBeforeElUpdated(from, to) {
      if (!window.Alpine) return;
      if (from.nodeType !== 1) return;
      // AlpineJS v2
      if (from.__x) window.Alpine.clone(from.__x, to);
      // AlpineJS v3
      if (from._x_dataStack) window.Alpine.clone(from, to);
    },
  },

Showing Posts 6 to 1

mchlsync

mchlsync

I got this error if I use <%= @count %>. I tried x-data={"{ count: #{@count} }" too but it doesn’t show the actual value in Chrome dev tool. it is not working as well.

Make sure the attribute is properly closed. This may also happen if
there is an EEx interpolation inside a tag, which is not supported.
Instead of

<div <%= @some_attributes %>>
</div>

do

<div {@some_attributes}>
</div>

Where @some_attributes must be a keyword list or a map.

c4710n

c4710n

This workaround would be fine if the Alpine component only contains shadowed assigns of LiveView.

But, if the Alpine component is maintaining its own states besides of the shadowed assigns of LiveView. Every time the shadowed assigns of LiveView updates, the Alpine component’s own states will be reset by window.Alpine.initTree(to).

That’s not expected.

For example:

<div x-data={"{ count: #{@count} }"}></div>                # fine
<div x-data={"{ count: #{@count}, showTip: true }"}></div> # bad

So, I prefer the workaround provided by simoncocking.

larryweya

larryweya

I struggled for a sometime with this issue, including using x-init to set the value without success, the only thing that has worked is the addition of window.Alpine.initTree(to) after the window.Alpine.clone(from, to) call.

polypush135

polypush135

I’ve tried this but with no luck

I have seen that if you call Alpine start again it re initializes and shows the correct data. My thought here is this is due to the way we are calling clone or what needs to happen to correctly re initialize

Any how heres my reproducible code.
https://github.com/MorphicPro/morphic_pro

Edit: so this looks like a possible solution Alpine.initTree()

Edit Edit:
Ok so I found it :tada:

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

and you don’t need to use x-init, Also heres something odd., look at what happens if you only use window.Alpine.initTree(to) But after inspecting the _x_dataStack array I’m not sure if that’s the correct way to do this.
Also this Expose the initTree method by KevinBatdorf · Pull Request #1648 · alpinejs/alpine · GitHub

polypush135

polypush135

Thanks for showing this new dom watcher. I was not seeing any console logs so I assumed it had not changed, but infact it was this that was the source of my issues.

simoncocking

simoncocking OP

So we’ve identified a workaround; the x-data behaviour in Alpine v3 appears to have changed, but it can be achieved as follows:

<div id="counter" x-data="{count: 0}" x-init="count = <%= @count %>">
  ...
</div>
— All posts loaded —

Where Next? Top

Trending in Questions Top

katta
I having some trouble figuring out if I have set myself too strict of standards for my production server. Currently I can handle 75% of r...
New
brecabral
Documentation While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
achenet
Hello, I’m trying to build a basic Phoenix web-app, and I’d like to use Tailwind. However, when I launch mix phx.server, I get an error...
New
kpanic
Hi everyone, I am toying with the idea of building a “match maker” for giving personal help to people that wants to start coding. I sta...
New
asweet-confluent
I recently noticed that Elixir’s Logger defaults its primary log level to :debug when no :logger, :level application configuration is pre...
New
Cxx-mlr
I’m working on a small exercise involving update_in/3, and I came up with this solution: data = %{ name: "Periodic Table", category:...
New
ChrisAmelia
I’ve got trouble wrapping my head around the order in which functions are called in this snippet (from Phoenix’s authentication): toke...
New

Other Trending Topics Top

GenericJam
Edit: 2026 May 15 - This post is archived. Mob is alive!! Main docs: mob v0.7.11 — Documentation A bit of explanation for the slightly c...
New
JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
mhanberg
Hi everyone! The first release candidate for the Expert language server project is now available! We’ve published a press release detai...
New
budgie
A little off-topic, but I feel like people here have a good head on their shoulders. I used to be quite good at making software. Was luc...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews