CherryPoppins

CherryPoppins

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;
        });
      });
    }
  });
};

Showing Posts 1 to 10

ruslandoga

ruslandoga

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.

akoutmos

akoutmos

Author of Build a Weather Station with Elixir and Nerves

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. 🌲 · GitHub. With the release of Alpine 3.x though, what functionality Spruce offered is now supported directly in AlpineJS.

CherryPoppins

CherryPoppins OP

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!

msimonborg

msimonborg

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

CherryPoppins

CherryPoppins OP

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.

msimonborg

msimonborg

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.

chocolatedonut

chocolatedonut

This is vaguely similar to what I’m experiencing. Here’s a minimal project that reproduces the issue. I’d be grateful if anyone can take a look. :pray:

The issue is that when
A) rendering directly from a LiveView, everything works. But when
B) the rendering is moved to a LiveComponent, Alpine’s x-show (or something else) “breaks”: the search results still show, but not immediately on typing (only after users also presses the Enter key).

In other words, moving the render/1 shown below (as well as some few handle_event/3) from

  • A) a :live_view to
  • B) a :live_component

somehow breaks and Alpine’s x-show doesn’t show the <ul> immediately. It does show up but only if one also then presses say the Enter key, or defocuses-and-refocuses the search <input> with a mouse, etc.

def render_from_live_view(assigns) do
  ~H"""
  <div class="flex-col relative" x-data="{open: false}">
    <p>Using LiveView, the result will immediately show below, without needing to also press Enter key.</p>
    <form id="search-form" phx-submit="submit">
      <input
        placeholder="type 'a'"
        name="search-input"
        id="search-input"
        class="block border border-black"
        phx-change="search"
        phx-hook="FocusOnMounted"
        x-on:focus="open = true"
      />
    </form>
    <ul :if={@search_results != []} x-show="open">
      <li :for={result <- @search_results}><%= result %></li>
    </ul>
  </div>
  """
end

And the relevant lines from app.js:

import Alpine from "alpinejs"
window.Alpine = Alpine
Alpine.start()

let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content")
let liveSocket = new LiveSocket("/live", Socket, {
  params: {_csrf_token: csrfToken},
  hooks: {
    FocusOnMounted: {
      mounted() {
        this.el.focus();
      }
    },
  },
  dom: {
    onBeforeElUpdated(from, to) {
      if (from._x_dataStack) {
        window.Alpine.clone(from, to);
      }
    }
  }
})

Visually, I only see the following differences in the relevant HTML between the :live_view (left) and the :live_component (right):

Other somewhat related threads:

chocolatedonut

chocolatedonut

I’ve fixed the error reported in my message above by merely adding an id attribute on the element that also has Alpine’s x-data attribute. Why was/is the missing id attribute an issue for Alpine? (It seems like any id attribute will do.)

It’s unclear why is it problematic if an id attribute isn’t explicitly provided. In other words, how come the LiveComponent-generated “id” doesn’t suffice? This is seen in my previous message above, on the image diff of two HTMLs; specifically, the auto-generated id is in the top red rectangle.

Investigating the LiveSocket’s dom object, specifically the onBeforeElUpdated(from, to) callback (as seen in app.js above) does suggest there’s some difference already on the initial load, before any user interaction: on load, the from and to elements only get printed in the console when the mentioned id is given (otherwise not).

Seems like I should first figure out why onBeforeElUpdated does (not) get called when id is (not) provided. Please let me know if you see where I’m misusing Alpine and/or LV; I’ll admit already that having both LV’s :if= and Alpine’s x-show on the same element seems superfluous and asking for trouble. :slight_smile:


PS. For Alpine, I’ve looked into docs at 1) the “Essentials” section, 2) on $id, 3) on x-id directive, and 4) on Alpine.data (and glanced at other sections as well) and couldn’t find any mention of a required id attribute, or anything that would explain this behaviour.

cmo

cmo

Or put an id on all alpine scopes.

And yes, you’re asking for (and getting) trouble. Personally, I ripped alpine out first chance I got. There is a certain elegance to alpine but it’s not worth the headache for me.

chocolatedonut

chocolatedonut

Thanks for the reply! Indeed, I’m already eyeing if/how JS Commands & Hooks might be able to replace Alpine.

Still, I’d be curious to read from you or anyone that knows why exactly is the above behaviour different between
A) a LiveComponent-provided id attribute (seen on the image above, in the top red rectangle), vs.
B) my own, explicitly set id attribute (on the same HTML element, the top-level <div>)

Where Next? Top

Trending in Questions Top

Blokh
Hey guys, I’ve got a huge CSV ( around 10 GB ) that needs to be processed hourly Do you guys have any suggestions what is the best prac...
New
RSP87
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
kszambelanczyk
Hello! Could someone please give me a help/sample code, how to delete a file from s3 using waffle/waffle_ecto from Phoenix app. I creat...
New
RemyXRenard
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
New
samoloth
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New
FlyingNoodle
If a change or preparation module uses Ash.Changeset.get_argument/2 or Ash.Query.get_argument/2 (or any of the other get_argument functio...
New

Other Trending Topics Top

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
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge &amp; Solve. They are GUI (Emerge) and State management (S...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews