Search code examples
drop-down-menularavel-livewire

how to create 2 Level Dependent Dropdowns like country and capital using laravel livewire?


I am new to laravel livewire and I am trying to create a 2 level dropdown menu (Country and capital) with livewire Components but with no luck I am not getting the resault I want.

This is my CountryDropdown.php code:

class CountryDropdown extends Component
{
    public $selectedCountry;
    public function render()
    {
        $countries = Wilaya::all();
        return view('livewire.country-dropdown', compact('countries'));
    }

    public function updatedSelectedCountry($id){
        $this->emit('countrySelected', $id);
    }
}

and this is my CapitalDropdown.php code:

class CapitalDropdown extends Component
{
    public $selectedCountry;
    public $capitals;

    protected $listeners = ['countrySelected' => 'render'];

    public function render()
    {
        //dd($this->selectedCountry);
        if($this->selectedCountry){
            $this->capitals = Commune::where('wilaya_id', $this->selectedCountry)->get();
        } else {
            $this->capitals = [];
        }
        return view('livewire.capital-dropdown');
    }
}

As you can see I am getting all countries in the CountryDropdown.php and it's working fine but whenever I Choose A country I don't get it's capital.

This is my country-dropdown.blade.php code

<div>
    <div class="form-group row">
        <select id="wilaya" wire:model="selectedCountry" class="w-full dropdown-toggle  items-center justify-between rounded-lg border border-jacarta-100
             bg-white py-3 px-3 dark:border-jacarta-600 dark:bg-jacarta-700 dark:text-jacarta-300">
            <option value="" selected>---Choisir---</option>
            @foreach ($countries as $wilaya)
            <option value="{{ $wilaya->id }}">{{ $wilaya->nom }}</option>
            @endforeach
        </select>
    </div>
</div>

and this is my capital-dropdown.blade.php code :

<div>
    <div class="form-group row">
        <div class="col-md-6">
            <select class="w-full dropdown-toggle  items-center justify-between rounded-lg border border-jacarta-100
            bg-white py-3 px-3 dark:border-jacarta-600 dark:bg-jacarta-700 dark:text-jacarta-300">
                <option value="">Choisissez la commune</option>
                @foreach($capitals as $commune)
                <option value="{{ $commune->id }}">{{ $commune->nom }}</option>
                @endforeach
            </select>
        </div>
    </div>
</div>

I haven't tried anything since I am new to livewire and I still don't understand what is the issues ? So Please feel free to help.

Thanks.


Solution

  • If you are using Livewire 3, you must apply some changes because your code is made for Livewire 2.

    In Livewire 3 wire:model is deferred by default, so you must add the live modifier to it. Also instead of emit() you must use dispatch().

    In country-dropdown.blade.php apply this change:

    <select id="wilaya" wire:model.live="selectedCountry" ....
    

    and I suppose that you will also add a wire:model to the capital-dropdown.


    CountryDropdown.php

    class CountryDropdown extends Component
    {
        public $selectedCountry;
    
    
        public function updatedSelectedCountry($id) {
            $this->dispatch('countrySelected', countryId: $this->selectedCountry);
        }
    
    
        public function render()
        {
            $countries = Wilaya::all();
            return view('livewire.country-dropdown', compact('countries'));
        }
    }        
    

    CapitalDropdown.php

    class CapitalDropdown extends Component
    {
        protected $selectedCountry = null;
        public $capitals;
    
        protected $listeners = ['countrySelected' => 'setCountry'];
    
    
        public function setCountry($countryId)
        {
            $this->selectedCountry = $countryId;
        }
    
    
        public function render()
        {
            $this->capitals = Commune::where('wilaya_id', $this->selectedCountry)->get();
    
            return view('livewire.capital-dropdown');
        }
    }
    

    Instead of $listeners you can also use the #[On] php attribute:

    .....
    use Livewire\Attributes\On;
    
    class CapitalDropdown extends Component
    {
        .....        
    
        #[On('countrySelected')]
        public function setCountry($countryId)
        {
            $this->selectedCountry = $countryId;
        }
    
        .....