wire-elements / modal

Livewire component that provides you with a modal that supports multiple child modals while maintaining state.
https://wire-elements.dev
MIT License
1.14k stars 133 forks source link

emit does not fire on boot/mount/booted methods #152

Closed loranger closed 2 years ago

loranger commented 2 years ago

Hi,

I dev a website with a shopping cart. On the product page, there is a button which add the current product to the cart (session), and open the cart modal in order to display the cart content and allow the user to update the quantities and manage selected products.

On the website layout, there is also a cart button (dropdown) which display the current amount of products in the cart. This is a very simple livewire component which count the products in the cart and display the number.

I could use polling in order to get the button up-to-date when I add a product in cart, but I prefered to manually broadcast event from the cart modal to the cart button. Events fired from the modal content works, but not from the modal lifecycle itself and I can't figure out why.

Here is my AddToCart modal component:

<?php

namespace App\Http\Livewire;

use LivewireUI\Modal\ModalComponent;

class AddToCart extends ModalComponent
{

    public $products;

    public function boot()
    {
        $this->products = session('cart', collect([]));
    }

    public function mount($product)
    {
        $quantity = $this->products->get($product, 0);
        $this->setProductToCart($product, ++$quantity);
    }

    public function remove($product)
    {
        $this->setProductToCart($product, 0);
    }

    public function setProductToCart($product, Int $quantity)
    {
        if ($quantity < 1) {
            $this->products->forget($product);
        } else {
            $this->products->put($product, $quantity);
        }
        session(['cart' => $this->products]);

        $this->emit('cartUpdated');
    }

    public function render()
    {
        return view('livewire.add-to-cart');
    }
}

As you can see, everything is done inside the setProductToCart which is the one which emit the cartUpdated event. This event is listened by the CartButton.

When the modal is opened, changing quantities or removing products from cart works fine. The CartButton listen the event and displays the new cart items count.

But the very first call, the one cascaded from the setProductToCart called from the mount method seems to never be fired. I tried calling this event from boot, or booted method and the result remains silent.

I also tried to do the same thing using a Livewire component instead of a ModalComponent and it works, so I thinks it's a modal bug, but I can't fgure out why or where…

I tried to test the issue, as unit as possible and made a very simple laravel project, if you want to try

PhiloNL commented 2 years ago

This is related to Livewire, you can't emit from the mount or boot method

emit won't work in the mount function or render function (during the first load) as Livewire's javascript isn't running yet. But it will work in the render function on subsequent requests.

loranger commented 2 years ago

Understood !

Thanks for pointing the issue. Is there a way to hook a modal event instead ? Maybe a opened event or something ?

I tried to add a wire:init="$emit('cartUpdated')" on the root div of my modal content, but it failed with a Unknown method Exception. I tried with a wire:init="Livewire.emit('cartUpdated')" but the result is the same.

I now use a wire:init="refreshCart" which lokks like this, but I'm definitely not satisfied:

public function refreshCart()
{
    $this->emit('cartUpdated');
}
PhiloNL commented 2 years ago

I use a similar approach; not sure if there is any other way to do this. Feel free to update this issue if you find an alternative solution.

TilsonM17 commented 6 months ago

Understood !

Thanks for pointing the issue. Is there a way to hook a modal event instead ? Maybe a opened event or something ?

I tried to add a wire:init="$emit('cartUpdated')" on the root div of my modal content, but it failed with a Unknown method Exception. I tried with a wire:init="Livewire.emit('cartUpdated')" but the result is the same.

I now use a wire:init="refreshCart" which lokks like this, but I'm definitely not satisfied:

public function refreshCart()
{
    $this->emit('cartUpdated');
}

Dude, you save my life, this work for me.