Liveview blowing up Alpine state?

I’m having an issue with alpinejs where I am seemingly loosing alpine after performing a task where liveview basically validates a changeset and reassigns/updates @changeset, I don’t think this part is important to the issue. While this changeset is getting validated I have a loader that uses x-show to show it or hide it based on a bool value from an Alpine.store. The loader is turn on with an @click and turned off by the liveview sending a message to a hook that changes the bool in Alpine.store. The loader is getting stuck to on/show (or its natural state of display: block) because I am loosing that x-show. Typing Alpine.start() into the console fixes the issue so I know it is alpine getting uninitialized or not loading back up or something. Anyone else having this issue with liveview and alpine?

Alpine setup:

import Alpine from 'alpinejs'

window.Alpine = Alpine
Alpine.start()

...

const liveSocket = new LiveSocket('/live', Socket, {
      params: {_csrf_token: csrfToken},
      dom: {
        onBeforeElUpdated(from, to) {
          if (from._x_dataStack) {
            window.Alpine.clone(from, to)
            window.Alpine.initTree(to)
          }
        },
      },
      hooks: Hooks,
    })

May or may not be relevant to this issue but I am also getting this error sometimes in the console on this liveview:

module.esm.js:2288 Uncaught (in promise) TypeError: i is not a function at module.esm.js:2288

the function, which seems to be coming from alpine, the error is referring to with the call to i() (23 lines down)

window.Element.prototype._x_toggleAndCascadeWithTransitions = function(el, value, show, hide) {
  let clickAwayCompatibleShow = () => requestAnimationFrame(show);
  if (value) {
    el._x_transition ? el._x_transition.in(show) : clickAwayCompatibleShow();
    return;
  }
  el._x_hidePromise = el._x_transition ? new Promise((resolve, reject) => {
    el._x_transition.out(() => {
    }, () => resolve(hide));
    el._x_transitioning.beforeCancel(() => reject({isFromCancelledTransition: true}));
  }) : Promise.resolve(hide);
  queueMicrotask(() => {
    let closest = closestHide(el);
    if (closest) {
      if (!closest._x_hideChildren)
        closest._x_hideChildren = [];
      closest._x_hideChildren.push(el);
    } else {
      queueMicrotask(() => {
        let hideAfterChildren = (el2) => {
          let carry = Promise.all([
            el2._x_hidePromise,
            ...(el2._x_hideChildren || []).map(hideAfterChildren)
          ]).then(([i]) => i());
          delete el2._x_hidePromise;
          delete el2._x_hideChildren;
          return carry;
        };
        hideAfterChildren(el).catch((e) => {
          if (!e.isFromCancelledTransition)
            throw e;
        });
      });
    }
  });
};
1 Like

Try removing Alpine.initTree(to) from onBeforeElUpdated. I had similar problems with it. The errors you are seeing might be easier to debug if you enable source maps.

I am guessing that since you are using a changeset, that the DOM element that you are interacting with is a form. In the past, I have had issues with Alpine state stored on the form tags (iirc Morphdom deals with patching forms differently that it does patching other HTML tags).

To circumvent the Morphdom form issues, I leveraged Spruce to store the Alphine state outside of the form tag GitHub - ryangjchandler/spruce: A lightweight state management layer for Alpine.js. 🌲. With the release of Alpine 3.x though, what functionality Spruce offered is now supported directly in AlpineJS.

So I am actually using Alpine.state('loader', false) to manage state here, initialized in a hook. My loader is inside of a form though so this could be the issue. I’ll have to do a bit of refactoring to test it out, but thanks for the lead!

Have you tried adding phx-update=“ignore” to the loader element tag? I’m Not actually sure if this would work on an element inside the form component, but might be worth trying

Yes, I tried pretty much everything back then. We ended up abandoning Apline because of too many issues at the time. It may be more stable now, this was quite a while ago and they seemed to be constantly evolving.

Sorry, I just realized how old this was! It showed up on my suggested feed and didn’t realize the date : ) I hope you found something that works for you. Alpine is working well for me today but honestly I just started with it.