where are your wire:key ?
Dec 17, 2023
3
Level 1
Livewire 4-level dependent dropdown not working reliably.
Hello, I'm very new to it all and am trying to set up a four level dependent dropdown. I followed a tutorial for a three level dropdown and also checked the code in every other tutorial I found on these dropdowns (few and far between) but they all looked roughly the same.
I have two main issues:
- When using orderBy('name') on get() query, it works fine the first time around but when I select an item it changes to what would've been in that spot without orderBy. I don't understand why this is happening, why is it loading it twice and isn't it using the same logic both times anyway? I should note it only changes graphically, the second dropdown is loaded correctly for the (first) selection. I thought it might also be causing issue two, but if I remove orderBy, issue 2 still happens.
- It seems to consistently "crash" when I select a different sublevel the first time closing everything, but works okay if you keep changing values (most of the time).
My livewire component:
<div class="col-span-full">
{{-- Make --}}
<div class="sm:col-span-full">
<div class="relative mt-2">
<select wire:model.live="selectedMake" id="make" name="make" autocomplete="off" class="block px-3 pb-3.5 pt-5 w-full text-sm text-gray-900 bg-transparent rounded-[5px] border-1 border-gray-300 appearance-none dark:border-gray-600 dark:focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-inset focus:border-indigo-600 peer">
<option>Select make</option>
@foreach ($makes as $id => $make)
<option value="{{ $make->id }}">{{ $make->name }}</option>
@endforeach
</select>
<label for="make" class="absolute text-xs text-gray-500 dark:text-gray-400 duration-300 transform -translate-y-4 scale-70 top-5 z-10 origin-[0] px-2 peer-focus:px-2 peer-focus:text-blue-600 peer-focus:dark:text-blue-500 peer-placeholder-shown:scale-100 peer-placeholder-shown:-translate-y-1/2 peer-placeholder-shown:top-7 peer-focus:top-5 peer-focus:scale-70 peer-focus:-translate-y-4 rtl:peer-focus:translate-x-1/4 rtl:peer-focus:left-auto start-1">Make</label>
</div>
</div>
{{-- Car --}}
@if(!is_null($selectedMake))
<div wire:transition class="sm:col-span-full">
<div class="relative mt-2">
<select wire:model.live="selectedCar" id="car" name="car" autocomplete="off" class="block px-3 pb-3.5 pt-5 w-full text-sm text-gray-900 bg-transparent rounded-[5px] border-1 border-gray-300 appearance-none dark:border-gray-600 dark:focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-inset focus:border-indigo-600 peer">
<option>Select car</option>
@foreach ($cars as $car)
<option value="{{ $car->id }}">{{ $car->name }}</option>
@endforeach
</select>
<label for="car" class="absolute text-xs text-gray-500 dark:text-gray-400 duration-300 transform -translate-y-4 scale-70 top-5 z-10 origin-[0] px-2 peer-focus:px-2 peer-focus:text-blue-600 peer-focus:dark:text-blue-500 peer-placeholder-shown:scale-100 peer-placeholder-shown:-translate-y-1/2 peer-placeholder-shown:top-7 peer-focus:top-5 peer-focus:scale-70 peer-focus:-translate-y-4 rtl:peer-focus:translate-x-1/4 rtl:peer-focus:left-auto start-1">Car</label>
</div>
</div>
@endif
{{-- Generation --}}
@if(!is_null($selectedCar))
<div wire:transition class="sm:col-span-full">
<div class="relative mt-2">
<select wire:model.live="selectedGeneration" id="generation" name="generation" autocomplete="off" class="block px-3 pb-3.5 pt-5 w-full text-sm text-gray-900 bg-transparent rounded-[5px] border-1 border-gray-300 appearance-none dark:border-gray-600 dark:focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-inset focus:border-indigo-600 peer">
<option>Select generation</option>
@foreach ($generations as $generation)
<option value="{{ $generation->id }}">{{ $generation->name }}</option>
@endforeach
</select>
<label for="generation" class="absolute text-xs text-gray-500 dark:text-gray-400 duration-300 transform -translate-y-4 scale-70 top-5 z-10 origin-[0] px-2 peer-focus:px-2 peer-focus:text-blue-600 peer-focus:dark:text-blue-500 peer-placeholder-shown:scale-100 peer-placeholder-shown:-translate-y-1/2 peer-placeholder-shown:top-7 peer-focus:top-5 peer-focus:scale-70 peer-focus:-translate-y-4 rtl:peer-focus:translate-x-1/4 rtl:peer-focus:left-auto start-1">Generation</label>
</div>
</div>
@endif
{{-- Powertrain --}}
@if(!is_null($selectedGeneration))
<div wire:transition class="sm:col-span-full">
<div class="relative mt-2">
<select wire:model.live="selectedPowertrain" id="powertrain" name="powertrain" autocomplete="off" class="block px-3 pb-3.5 pt-5 w-full text-sm text-gray-900 bg-transparent rounded-[5px] border-1 border-gray-300 appearance-none dark:border-gray-600 dark:focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-inset focus:border-indigo-600 peer">
<option>Select Powertrain</option>
@foreach ($powertrains as $powertrain)
<option value="{{ $powertrain->id }}">{{ $powertrain->name }}</option>
@endforeach
</select>
<label for="powertrain" class="absolute text-xs text-gray-500 dark:text-gray-400 duration-300 transform -translate-y-4 scale-70 top-5 z-10 origin-[0] px-2 peer-focus:px-2 peer-focus:text-blue-600 peer-focus:dark:text-blue-500 peer-placeholder-shown:scale-100 peer-placeholder-shown:-translate-y-1/2 peer-placeholder-shown:top-7 peer-focus:top-5 peer-focus:scale-70 peer-focus:-translate-y-4 rtl:peer-focus:translate-x-1/4 rtl:peer-focus:left-auto start-1">Powertrain</label>
</div>
</div>
@endif
</div>
CarSelect.php
<?php
namespace App\Livewire;
use App\Models\Make;
use App\Models\Car;
use App\Models\Generation;
use App\Models\Powertrain;
use Livewire\Component;
class CarSelect extends Component
{
// Defining all the variables.
public $makes;
public $cars;
public $car;
public $generations;
public $powertrains;
public $years;
public $transmissions;
public $fuels;
// Defining the default select states.
public $selectedMake = null;
public $selectedCar = null;
public $selectedGeneration = null;
public $selectedPowertrain = null;
// When the component is loaded, we get all the car makes.
public function mount()
{
$this->makes = Make::orderBy('name')->get();
}
// Functions that update the selected.
public function updatedSelectedMake($make)
{
if (!is_null($make)) {
$this->cars = Car::where('make_id', $make)->orderBy('name')->get();
$this->car = $this->cars->first()->id ?? null;
}
$this->selectedCar = null;
}
public function updatedSelectedCar($car)
{
if (!is_null($car)) {
$this->generations = Generation::where('car_id', $car)->orderBy('name')->get();
$this->appendYearsToGenerations();
}
$this->selectedGeneration = null;
}
public function updatedSelectedGeneration($generation)
{
if (!is_null($generation)) {
$this->appendYearsToGenerations();
$this->powertrains = Powertrain::where('generation_id', $generation)->orderBy('name')->get();
}
$this->selectedPowertrain = null;
}
public function updatedSelectedPowertrain($powertrain)
{
if (!is_null($powertrain)) {
$this->appendYearsToGenerations();
}
}
// Function to add the Generation min-max years, gotten from the Powertrains.
public function appendYearsToGenerations()
{
foreach ($this->generations as $generation) {
$minYear = Powertrain::where('generation_id', $generation['id'])->min('year_begin');
$maxYear = Powertrain::where('generation_id', $generation['id'])->max('year_end');
$generationNameWithYears = $generation->name . ' (' . $minYear . ' - ' . $maxYear . ')';
$generation->name = $generationNameWithYears;
}
}
// Gets the view file for this component.
public function render()
{
return view('livewire.car-select');
}
}
I know it's pretty repetitive and could probably use a refactor but I'd appreciate any help in at least getting the dang thing to work reliably fist, thank you in advance!
PS: I made a fancy gif to illustrate but I still can't post links even though I've been waiting three days to post this :(
Level 122
Please or to participate in this conversation.