livewire: Alpine does not initialise inside a blade conditional, after re-render.
This applies to a page re-render only, after some logic is performed. For example:
<button wire:click="setModelToTrue()">click</button>
@if($model->itIsTrue())
<div x-data="{ open: false }">
<button @click="open = true">Show More...</button>
<ul x-show="open" @click.away="open = false">
<li>
<button wire:click="archive">Archive</button>
</li>
<li>
<button wire:click="delete">Delete</button>
</li>
</ul>
</div>
@endif
On a page load/full refresh with “itIsTrue()” all works as expected.
However, If we click the button to turn “it to true” dynamically, the re-render results in the alpine component not being initialized and the dropdown does not work.
I have tried livewire keys, element ID’s, extra div elements (as in the troubleshooting section) but I just can’t get anything working.
About this issue
- Original URL
- State: closed
- Created 4 years ago
- Comments: 35 (2 by maintainers)
Commits related to this issue
- Alpine does not initialise inside a blade conditional, after re-render Similar case ref https://github.com/livewire/livewire/issues/920 — committed to lam0819/filament by lam0819 2 years ago
So I have found out that I can initialize the new Alpine component using:
We can do this automatically after the dom is updated:
It has taken me close to a full day to get to this solution. Considering Livewire and Alpine are supposed to place nice together, maybe this should be considered to be part of the core or at least of the docs.
@calebporzio, what do you think? I am happy to PR the docs if you agree.
What I’ve ended up doing is adding a
wire:keyto the Alpine component which fixed the issue.I’m running into some similar issues with Livewire re-rendering the component. @Dimitris947 have you also used this with Alpine v3? Because I get
discoverUninitializedComponents is not a function.Well, this issue do I also get a lot. The trick is to wrap a div around it like this:
And in my instance I even had to go as crazy as below to get it working with a foreach:
In my instance,
window.livewire.findwould always call theorderItemLivewireIdwhich was first clicked. This happened multiple times now to me in my project.But above code would solve it. My guess is that Alpine is not able to reregister x-on events correctly or something. I believe this is related in our case: https://github.com/alpinejs/alpine/issues/412
Fun fact: before I got it working like above, I could even inspect the DOM, and see the correct
orderItemLivewireId’s within thoselivewire.find()calls … But whichever id was first called via thelivewire.find, would fire always, even if the dom did not match.But I am pretty sure you could get it working with some unique id’s here and there, and not having to resort to manually booting Alpine components.
@calebporzio if you are interested in more sample code to reproduce this behavior, I am happy to provide it.
@adddz couple of things you can do.
If the component is in a loop You can use the
$loopparameter that Laravel has available for you:wire:key="{{ $loop->iteration }}". Or if you’re looping over posts you can just use the model ID like so:wire:key="{{ $post->id }}"If the component is not in a loop and you’re using the same component multiple times Just use something manual, like
wire:key="thing-1"andwire:key="thing-2"etc.I recently got weird behaviour because I had something within a blade
@ifstatement. In that case it can help to wrap that@ifstatement in a<div>. Also see https://laravel-livewire.com/docs/2.x/troubleshootingI have the same issue, a component that does something on x-init based on what it receives from x-data, passing a Livewire property. Also, doing:
document.addEventListener("livewire:load", function(event) { window.livewire.hook('afterDomUpdate', () => { window.Alpine.discoverUninitializedComponents((el) => { window.Alpine.initializeComponent(el); }); }); });does not work for me.I have the similar issue when the whole component is refreshed using
@this.call('$refresh');and the code insidex-initis never triggered.@calebporzio I can confirm this works with your example, but also that it does not work in my codebase, using the latest Livewire and Alpine as of 7/19/20. The only material differences I can spot are that my component has a function-call style data provider, and an init method, and the init method is async (speaks to camera input). Putting an x-data on the Livewire parent div definitely causes the component initializer to run, but the x-shows and such on the child elements do not work.
The only thing that resolves this is this code, from @booni3
window.livewire.hook('afterDomUpdate', () => { window.Alpine.discoverUninitializedComponents((el) => { window.Alpine.initializeComponent(el) }) })Until this is fixed, another simple work around, not better than the above, just super quick and simple, is to leave the component there on the page, and toggle display: none.
Note, I’m sure everyone is used to it but the css for display none comes from tailwindcss and is the “hidden” in the x-input.date element.
Code example:
here is the blade component with the JS, I like flatpickr better than pickaday, it also doesn’t use moment.js, but it doesn’t work well on brave browser “right now”, and date controls often behave weird on mobile devices, so the idea is if it’s on a mobile device, just default to the device date implementation
The
wire:keysolution on the same element that defines thex-dataattribute works but it kind of got buried in the conversation.Thank you. I agree that if you don’t set the
wire:keyand there’s an@ifconditional, some weird behavior might happen.@adddz see below (note the
wire:key). Note that this does not work when you define x-data outside the component as a function within<script></script>.REOPEN
Hey just added some similar code in a blade component to another project and this is still an issue 😦
Newest Livewire and alpine…
This works beautifully, if the component is visible on the page at the outset, however in some cases like a modal, that is added to the page with a @if loading is never set to false, and the button renders with the spinner and doesn’t disable.
I know this is an old issue so I’m just putting this out here as there isn’t an example that breaks every time.
Have you verified you are including @livewireScripts BEFORE including AlpineJS in your app layout?
@bnrosa same here this does not work for me. But the CSS “hidden” trick by @roni-estein works for me very well.