I'm struggling to make Alpine and Phoenix LiveViews work seamlessly together.
After implementing the code below, I noticed that liveview phx-click
events are not triggering, even though Alpine functions properly. Additionally, Tailwind CSS animations do not behave as expected upon page load, but other Tailwind CSS properties style the elements correctly. There is a specific card on the page that should flip over when loaded, but it remains static.
When I remove Alpine.start()
, Alpine does not function correctly initially, but starts working after interacting with a liveview component link on the page resembling this:
<.link navigate={~p"/lessons"}>
. All Tailwind CSS, including animations, works perfectly in this scenario. It's worth noting that some parts of Alpine do work upon page load. A particular element with x-show="open"
stays hidden due to its parent having x-data="{ open: false }"
. While the @click="open - !open"
successfully alters the value of open
, the issue lies in x-show
failing to adjust the display: none
property as open
changes.
If I exclude liveSocket.connect()
, Alpine operates correctly at the start, but then the liveviews stop functioning. The card fails to flip and loads overturned instead. It's puzzling how disconnecting the socket affects the CSS.
Moving Alpine.start()
just after liveSocket.connect()
causes Alpine to malfunction upon page load, while all Tailwind CSS styles and liveview functionalities work properly.
Interestingly (or rather confusingly), eliminating both Alpine.start()
and liveSocket.connect()
allows Alpine to work flawlessly at page load, but subsequently disables the liveviews.
Researching the functionality of Alpine.start()
has proven challenging, and understanding its interaction with liveSocket.connect()
is even more perplexing.
Relevant snippet from app.js
import Alpine from '../vendor/alpine'
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: Hooks,
dom: {
onBeforeElUpdated(from, to) {
if (from._x_dataStack) {
window.Alpine.clone(from, to)
}
}
},
})
// connect if there are any LiveViews on the page
liveSocket.connect()
Relevant piece from nav.ex
, an active component
<div class="relative ml-3" x-data="{ open: false }">
<button
@click="open = !open"
@click.outside="open = false"
type="button"
class="flex max-w-xs items-center rounded-full bg-white text-sm focus:outline-none focus:ring-2 focus:ring-green focus:ring-offset-2"
id="user-menu-button"
aria-expanded="false"
aria-haspopup="true"
>
<span class="sr-only">Open user menu</span>
<img
class="h-8 w-8 rounded-full"
src="https://i.kym-cdn.com/photos/images/facebook/001/431/201/40f.png"
alt=""
/>
</button>
<!--
Dropdown menu, show/hide based on menu state.
-->
<div
class="absolute right-0 z-10 mt-2 w-48 origin-top-right rounded-md bg-white py-1 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none"
role="menu"
aria-orientation="vertical"
aria-labelledby="user-menu-button"
tabindex="-1"
x-show="open"
x-transition:enter="transition ease-out duration-100"
x-transition:enter-start="transform opacity-0 scale-95"
x-transition:enter-end="transform opacity-100 scale-100"
x-transition:leave="transition ease-in duration-75"
x-transition:leave-start="transform opacity-100 scale-100"
x-transition:leave-end="transform opacity-0 scale-95"
x-cloak
>...</div>