Laravel 12
Livewire 3.6"
Hi,
I Found two posts with solution to this issue, but doesn't answer my issue.
The three cascading dropdowns (countries, states, cities) are rendered correctly in the create.blade form.
I can select a country and when the country is select, then, normally, the states dropdown should be enabled and the states belonging to the selected country should be populated. It doesn't happen, there's no activity in the inspector Network tab when I select the country and no message is generated with \log. But Livewire is functional, the package is listed with php artisan package:discover, files and folder are created etc.
Here the relevant files : TestComponent
<?php
namespace App\Livewire;
use Livewire\Component;
use App\Models\City;
use App\Models\Country;
use App\Models\State;
class TestComponent extends Component
{
#[\Livewire\Attributes\Reactive]
public $selectedCountry;
#[\Livewire\Attributes\Reactive]
public $selectedState;
public $states = [];
public $cities = [];
public function updated($property, $value)
{
\Log::info('Updated Property: ' . $property . ' - Value: ' . $value); // Log the updated property
if ($property === 'selectedCountry') {
$this->states = State::where('country_id', $value)->get();
// Log the retrieved states count
\Log::info('States for selected country ' . $value . ': ' . $this->states->count());
$this->selectedState = null; // Reset the selected state
$this->cities = []; // Clear cities when country is changed
// Log if no states are found
if ($this->states->isEmpty()) {
\Log::info('No states found for country ' . $value);
}
}
if ($property === 'selectedState') {
$this->cities = City::where('state_id', $value)->get();
// Log the retrieved cities count
\Log::info('Cities for selected state ' . $value . ': ' . $this->cities->count());
if ($this->cities->isEmpty()) {
\Log::info('No cities found for state ' . $value);
}
}
}
public function render()
{
return view('livewire.location-dropdown', [
'countries' => Country::all(), // Pass the countries to the view
'states' => $this->states,
'cities' => $this->cities,
]);
}
}
test-component.blade
<div>
<!-- Country Dropdown -->
<div class="form-group">
<label for="country" class="form-label">Country</label>
<select wire:model="selectedCountry" id="country" class="form-control">
<option value="">Select Country</option>
@foreach($countries as $country)
<option value="{{ $country->id }}">{{ $country->name }}</option>
@endforeach
</select>
</div>
<!-- State Dropdown -->
<div class="form-group">
<label for="state" class="form-label">State</label>
<select wire:model="selectedState" id="state" class="form-control" {{ $selectedCountry ? '' : 'disabled' }}>
<option value="">Select State</option>
@foreach($states as $state)
<option value="{{ $state->id }}">{{ $state->name }}</option>
@endforeach
</select>
</div>
<!-- City Dropdown -->
<div class="form-group">
<label for="city" class="form-label">City</label>
<select id="city" class="form-control" {{ $selectedState ? '' : 'disabled' }}>
<option value="">Select City</option>
@foreach($cities as $city)
<option value="{{ $city->id }}">{{ $city->name }}</option>
@endforeach
</select>
</div>
</div>
create.blade
<x-page-template bodyClass='g-sidenav-show bg-gray-200'>
@php
$isEdit = isset($bankAccount);
$title = $isEdit ? 'Edit Bank Information' : 'Create New Bank Information';
$route = $isEdit ? route('bankaccounts.update', $bankAccount) : route('bankaccounts.store');
@endphp
<x-auth.navbars.sidebar activePage="bank-account" activeItem="bank-info-management" activeSubitem="">
</x-auth.navbars.sidebar>
<main class="main-content position-relative max-height-vh-100 h-100 border-radius-lg ">
<!-- Navbar -->
<x-auth.navbars.navs.auth pageTitle="Bank Information Management"></x-auth.navbars.navs.auth>
<!-- End Navbar -->
<div class="container-fluid py-4">
<div class="row mt-4">
<div class="col-12">
<div class="card">
<!-- Card header -->
<div class="card-header">
<h5 class="mb-0">{{ $isEdit ? 'Update' : 'Add' }}
{{ $subscriber ? $subscriber->firstname . ' ' . $subscriber->lastname : 'Subscriber' }} Bank Information
</h5>
</div>
<div class="card-body">
<form method="POST" action="{{ isset($bankAccount) && $bankAccount ? route('bankaccounts.update', $bankAccount->id) : route('bankaccounts.store') }}">
@if(isset($bankAccount) && $bankAccount)
@method('PUT') <!-- Only needed for updates -->
@endif
@csrf
<div class="form-group col-12 col-md-6">
<div class="form-group col-12 col-md-6">
<label for="account_number_bank" class="form-label">Account No</label>
<input type="text" class="form-control border border-2 p-2 @error('account_number_bank') is-invalid @enderror" id="account_number_bank" name="account_number_bank" value="{{ old('account_number_bank', $bankAccount->account_number_bank ?? '') }}">
@error('account_number_bank')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="form-group col-12 col-md-6">
<label for="account_holder_name_bank" class="form-label">Account Holder Name</label>
<input type="text" class="form-control border border-2 p-2 @error('account_holder_name_bank') is-invalid @enderror"
id="account_holder_name_bank" name="account_holder_name_bank"
value="{{ old('account_holder_name_bank', $bankAccount->account_holder_name_bank ?? '') }}">
@error('account_holder_name_bank')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="form-group col-12 col-md-6">
<label for="name_bank" class="form-label">Name Bank</label>
<input type="text" class="form-control border border-2 p-2 @error('name_bank') is-invalid @enderror"
id="name_bank" name="name_bank"
value="{{ old('name_bank', $bankAccount->name_bank ?? '') }}">
@error('name_bank')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="form-group col-12 col-md-6">
<label for="address_bank" class="form-label">Address</label>
<input type="text" class="form-control border border-2 p-2 @error('address_bank') is-invalid @enderror"
id="address_bank" name="address_bank"
value="{{ old('address_bank', $bankAccount->address_bank ?? '') }}">
@error('address_bank')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<livewire:test-component />
<div class="form-group col-12 col-md-6">
<label for="zip" class="form-label">Zip Code</label>
<input type="text" class="form-control border border-2 p-2 @error('zip_bank') is-invalid @enderror"
id="zip" name="zip_bank"
value="{{ old('zip_bank', $bankAccount->zip_bank ?? '') }}">
@error('zip_bank')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<button type="submit" class="btn btn-primary mt-3 col-12 col-md-6">
{{ $isEdit ? 'Update' : 'Add' }}
</button>
<a class="btn bg-gradient-dark mb-0 mt-3 col-12 col-md-6" href="{{ url()->previous() }}">Back to Previous Subscriber</a>
<a class="btn bg-gradient-dark mb-0 mt-3 col-12 col-md-6" href="{{ route('subscribers.index') }}">Back to List of Subscribers</a>
</div>
<input type="hidden" name="user_id" value="{{ $bankAccount->user_id ?? $subscriber->user_id }}"><input type="hidden" name="user_id" value="{{ request()->query('user_id') }}">
</form>
</div> <!-- Closing card-body div -->
</div> <!-- Closing card div -->
</div> <!-- Closing col-12 div -->
</div> <!-- Closing row div -->
<x-auth.footers.auth.footer></x-auth.footers.auth.footer>
</div> <!-- Closing container-fluid div -->
</main>
<x-plugins></x-plugins>
@push('js')
<script src="{{ asset('assets') }}/js/plugins/perfect-scrollbar.min.js"></script>
@endpush
</x-page-template>
Would appreciate some help here, I have check the config/app, installed 2 times Livewire, created multiple new components with the same aim, but still no functioning cascading dropdowns.
Thank you in advance for your support.
Cheers,
Marc
In Livewire 3 wire:model is deferred by default. To allow an immediate call to the backend when a <select> changes, you must use the live modifier in wire:model.
For example:
<select wire:model.live="selectedCountry" id="country" class="form-control">
This way, when you update the <select> in the browser, the updated() method in the backend is called immediately, as expected.
The PHP attribute #[Reactive] for class properties, on the other hand, is used to propagate changes to a property of a parent component to a child component: this doesn't seem to be your case, so you can remove it.