Alpine: Element inside nested Alpine component with x-cloak disappears when Livewire re-renders

Created on 25 Apr 2020  路  15Comments  路  Source: alpinejs/alpine

I was not sure wether this belongs on the Alpine or Livewire repository. If this is not the right place I will happily move it over.

I have been building a comment section using Alpine and Livewire and am really impressed. There is just one challenge which has been keeping me busy for 2 days now. Users should be able to edit comments using a modal. However, the modal disappeared the moment Livewire processed the input.

ezgif com-video-to-gif-2

After hours trying to get to the core of the problem this is where I left.

<div x-data="{}">
    <div x-data="{show: false}">
        <button @click="show = true">Show input</button>

        <input x-show="show" x-cloak type="text" wire:model="value">
    </div>
</div>

ezgif com-video-to-gif

In this example, I expect the input element to persist which is the case when you remove the nesting or the x-cloak attribute. It would be great if anyone sees a connection. I am really curious to understand what's going on.

Most helpful comment

I think it's a Livewire bug (I believe the Alpine integration lives in that repository).

If you try this snippet

    <div x-data="{}">
        <div x-data="{show: false}">
            <button @click="show = true">Show input</button>
            <span x-text="show"></span>

            <input x-show="show" type="text" wire:model="value">
        </div>
    </div>

You can see that show gets reverted to false every time Livewire refreshes the Livewire component but it works correctly if your Alpine component is not nested.

I don't think x-cloak makes much difference here.

All 15 comments

Looking at it at first sight, can you try to not use @click on button. Instead use x-on:click since both livewire and Alpine uses same directive for click event. That might be the issue. Let's see how it goes. And why there is x-cloak?

    <div x-data="{show: false}">
        <button x-on:click="show = true">Show input</button>

        <input x-show="show" type="text" wire:model="value">
    </div>

Thanks! I tried it but it did not change the behavior. The example is over-simplified. The input element in a real app would be wrapped inside a modal. Without x-cloak the modal flickers before Alpine loads.

maybe you should have a look at this. Sounds like related to debounce

or Lazily Updating

<input type="text" wire:model.lazy="value">

I think it's a Livewire bug (I believe the Alpine integration lives in that repository).

If you try this snippet

    <div x-data="{}">
        <div x-data="{show: false}">
            <button @click="show = true">Show input</button>
            <span x-text="show"></span>

            <input x-show="show" type="text" wire:model="value">
        </div>
    </div>

You can see that show gets reverted to false every time Livewire refreshes the Livewire component but it works correctly if your Alpine component is not nested.

I don't think x-cloak makes much difference here.

Okay, it turned up that it might be more complicate than that.

Livewire, when updating its component, walks the old dom and clones the old Alpine components into the new ones.

The clone function in Alpine does

    clone: function clone(component, newEl) {
      if (!newEl.__x) {
        newEl.__x = new Component(newEl, component.getUnobservedData());
      }
    },

In that snippet. Livewire knows that he need to clone

  • parent component
  • child component

This happens sequentially and it does the parent first: when Alpine initialises the parent, because it finds a x-data in one of their children, it also initialise the nested component using the original data in your html then it tries to clone the child component but, because the component has already been initialised, it skips this phase.

Changing clone to

    clone: function clone(component, newEl) {
        newEl.__x = new Component(newEl, component.getUnobservedData());
    },

seems to fix the issue but I'm not familiar with the integration so I don't know if it would break something else.

cc. @calebporzio

Thanks for diving into this! You are right. x-cloak does not make a difference because now there is a x-text. If you remove x-text the snippet starts working as expected again. Just figured out that this is also the case for other directives like x-htmland x-bind.

I might be missing something here, but do you not just need to add wire:ignore to your element that keeps getting overwritten by livewire?

I might be missing something here, but do you not just need to add wire:ignore to your element that keeps getting overwritten by livewire?

Then you wouldn't be able to use wire:model="value" in the example above. My use case is also similar.

Wouldn't wire:ignore.self work?

Wouldn't wire:ignore.self work?

Not for me because it's the same element. A datepicker in a modal.

I've got the same issue with x-cloak and livewire component.
Without x-cloak eveything works well, but I need it to prehide elements.
gif

button:
```

Any solution to prehide elements?

I have the same behaviour with Phoenix LiveView. It's not related to x-cloak for me.

What @SimoTod suggested actually works for me.

@ssbb can you post a snippet for LiveView? 馃檹

@RxAssim I am just forked Alpine repo and did this changes.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

adevade picture adevade  路  3Comments

adinata-id picture adinata-id  路  4Comments

dkuku picture dkuku  路  5Comments

allmarkedup picture allmarkedup  路  4Comments

piotrpog picture piotrpog  路  3Comments