livewire / livewire

A full-stack framework for Laravel that takes the pain out of building dynamic UIs.
MIT License
22.09k stars 1.52k forks source link

Dynamically/conditionally mounting components in runtime #750

Closed mustafaaloko closed 4 years ago

mustafaaloko commented 4 years ago

I was loading a Livewire component inside an AlpineJS conditional x-if like below, once the condition evaluates to true, the dom for the component does load, but other functionalities don't work. I specifically checked model binding with wire:model which doesn't work. Debugging through Livewire source code, I noticed that the component inside x-if doesn't get initialized at all (I mean after x-if evaluates to true). With x-show everything works fine, cause everything gets initalized on page load.

...
<template x-if="open">
    @livewire('hello-world')
</template>
...

How can I mount/initialize components once they are dynamically added to the dom in runtime (not in initial page load)?

calebporzio commented 4 years ago

That's a great question. Can you just use x-if? Also, try window.livewire.rescan()

mustafaaloko commented 4 years ago

I had already tried that, didn't work. I tried window.livewire.rescan() both in the component which is inside x-if template and also to be sure in browser console too after the element showed up on the browser, but still, model binding doesn't work. Tried window.livewire.restart() too.

Also, I didn't get what you meant by just use x-if? I am already doing that.

mustafaaloko commented 4 years ago

Will still explore on my side to find a way for this.

mustafaaloko commented 4 years ago

So I just checked, window.livewire.rescan() works if the component is in root and not inside another component (not nested). But, it doesn't work for nested components. My first try was with a nested component, that's why it didn't work.

mustafaaloko commented 4 years ago

I think I understood and found why it isn't working, based on rescan() source, it only checks for the root component elements with no parents, which somehow makes sense, cause if it finds any new component element inside the ones in the root, they will be added recursively once the root component object is created and is initialized,

In my case, as there is no change in root component elements, it simply continues the loop and doesn't do anything about it, ignoring what changes might have happened inside the root component elements.

calebporzio commented 4 years ago

Sorry, I meant ‘x-show’

mustafaaloko commented 4 years ago

Well, in my specific scenario, I wanted to lazy load a few scripts (inside my Alpine component) only when the Livewire component (which is using the mentioned Alpine component inside itself) is rendered (e.g. on a modal view, or any other action). Do you have any suggestions to achieve that?

calebporzio commented 4 years ago

Lazy loading can be done using wire:init -> go here for more details: https://laravel-livewire.com/docs/defer-loading

assoft commented 4 years ago

Hey @calebporzio this is possible? or is there a way to do it?

app\Http\Livewire\Post\Base.php

Class Base extends Component {
    public $view = "index";

    public function render()
    {
        return view('livewire.post.base');
    }
}

resources\livewire\post\base.blade.php

<div>
    <select wire:model="view class="form-select">
        <option value="index">Index</option>
        <option value="edit">Edit</option>
    </select>
    <div class="view-area">
        @livewire("post.$view")
    </div>
</div>
assoft commented 4 years ago

This is work :) 👍

@switch($view)
    @case("create")
        @livewire("admin.posts.create")
        @break
    @case("edit")
        @livewire("admin.posts.edit")
        @break
    @default
        @livewire("admin.posts.index")
@endswitch
abdosaeedelhassan commented 3 years ago

hi, also i want to dynamic render render livewire component inside main component according to condition, i did it as follow

Screenshot from 2020-11-26 16-43-09

the above code already inside the main component, but not worked for me, in the browser inspect just display the livewire component as html tag, i need help, thank you

devilstars commented 3 years ago

I have the same issue with x-if (apline.js).

<template x-if="$store.modalAuth.open">
    <div class="fixed inset-0 z-30"
         x-transition:enter="transition ease-out duration-100"
         x-transition:enter-start="opacity-0 transform"
         x-transition:enter-end="opacity-100 transform"
         x-transition:leave="transition ease-in duration-100"
         x-transition:leave-start="opacity-100 transform"
         x-transition:leave-end="opacity-0 transform">
       <livewire:auth.modals.login />
    </div>
</template>

auth.modals.login is rendered correctly in the dom, but backend does nothing with it. No validation, no submit. It works just like a regular form without the livewire. I had to replace x-if with x-show to make it working again.

ehsantalkhouncheh commented 3 years ago

i have a same issue i saved livewire component name in database like this @livewire('component_name') and when i try to load it as a content of page it doesn't render and show me just as a text anybody have a solution??

divdax commented 3 years ago

Same here with a different scenario. If i add a livewire component-tag with javascript to the DOM it would be cool if i can "rescan" the dom to render the component correctly.