AlpineJS dropdown only toggles when liveSocket is not connected

Hi everyone,

I’m trying to display a dropdown menu using LiveView and AlpineJS. For the most part, things have worked well, but I’m encountering an issue with this dropdown implementation.

When I try to open and close the dropdown using AlpineJS, the variable is updated (as seen by a console.log statement), but the dropdown does not get displayed. For some reason, the x-show value change isn’t being registered for this case.

However, when I comment out the line: liveSocket.connect() in my app.js, then it works just as expected. Somehow AlpineJS and LiveView aren’t getting along, and I’d really appreciate any help in resolving this.

I’ve provided the relevant code below:

live/navbar_component.html.heex
          <div x-data="{openSettingsDropdown: false}">
            <button
              type="button"
              id="user-menu-button"
              :aria-expanded="openSettingsDropdown"
              aria-haspopup="true"
              x-on:click="openSettingsDropdown = !openSettingsDropdown; console.log(openSettingsDropdown);"
            >
            </button>
            <div
              x-cloak
              x-show="openSettingsDropdown"
              role="menu"
              aria-orientation="vertical"
              aria-labelledby="user-menu-button"
              tabindex="-1"
            >
              <a
                href="#"
                class="block px-4 py-2 text-sm text-gray-700"
                role="menuitem"
                tabindex="-1"
                id="user-menu-item-0"
              >
                Your Profile
              </a>
        </div>
app.js
import "phoenix_html";
// Establish Phoenix Socket and LiveView configuration.
import { Socket } from "phoenix";
import { LiveSocket } from "phoenix_live_view";
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, {
  dom: {
    onBeforeElUpdated(from, to) {
      if (from.__x) {
        window.Alpine.clone(from.__x, to);
      }
    },
  },
  params: { _csrf_token: csrfToken },
});

liveSocket.connect();

// expose liveSocket on window for web console debug logs and latency simulation:
// >> liveSocket.enableDebug()
// >> liveSocket.enableLatencySim(1000)  // enabled for duration of browser session
// >> liveSocket.disableLatencySim()
window.liveSocket = liveSocket;
});
1 Like

On the div with x-show, set phx-update="ignore". You need to set this when you’re messing with the DOM outside of LiveView.

Actually, you need it on the top level one because of the :ariaexpanded, which you’ll want to write as x-bind:ariaexpanded so you don’t upset the heex compiler.

Note that you can do some of this stuff with the LiveView.JS functions too.

Thank you! I’ll watch out for that in the future

FWIW, I kept trying for a solution that didn’t rely on phx-update="ignore" due to any child elements obviously not receiving updates with that approach.

Not sure if this was the root cause, but I used this dom setting in app.js:

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