When creating forms it is pretty common to use two <select>
dropdown fields depending on each other, with a parent-child relationship. In this tutorial, we will show to use Livewire Lifecycle Hooks to implement exactly that.
Also, we will make those <select>
inputs even better by using Virtual Select and Tom Select JavaScript libraries.
The final result we will achieve using Tom Select:
Table of Contents
- 2-level Dropdown
- 3-level Dropdown
- Edit Form with 3-level Dropdown
- Virtual Select library
- Tom Select library
And, of course, link to the repository will be posted at the end of this tutorial.
Let's go?
2-level Dropdown
First, we will take a look at two select inputs. For this we will have two Models:
- Category (string: name).
- Product (string: name, foreignId: category_id).
And a Livewire component called CategoryProduct
.
After selecting Category
we will show all Products
that belong to that category.
First, we need to set two public properties $categories
and $products
where the list of both will be set. And when the component gets mounted we set them.
use Livewire\Component;use App\Models\Category;use Illuminate\Support\Collection;use Illuminate\Contracts\View\View; class CategoryProduct extends Component{ public Collection $categories; public Collection $products; public function mount(): void { $this->categories = Category::all(); $this->products = collect(); } public function render(): View { return view('livewire.category-product'); }}
And simple form with two select inputs.
<div> <form wire:submit.prevent="submit"> <div> <label class="block text-sm font-medium text-gray-700" for="category"> Category* </label> <select wire:model="category" name="category" class="mt-2 w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500" required> <option value="">-- choose category --</option> @foreach ($categories as $category) <option value="{{ $category->id }}">{{ $category->name }}</option> @endforeach </select> </div> <div class="mt-4"> <label class="block text-sm font-medium text-gray-700" for="product"> Product* </label> <select wire:model="product" name="product" class="mt-2 w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500" required> @if($products->isEmpty()) <option value="">-- choose category first --</option> @endif @foreach ($products as $product) <option value="{{ $product->id }}">{{ $product->name }}</option> @endforeach </select> </div> <x-primary-button class="mt-4"> Submit </x-primary-button> </form></div>
Now we bind these inputs to category
and product
.
class CategoryProduct extends Component{ public ?int $category = null; public ?int $product = null; public Collection $categories; public Collection $products; public function mount(): void { $this->categories = Category::all(); $this->products = collect(); } // ...}
After selecting the category we get its ID by which we can search for the products. For this, we need to use Lifecycle Hooks.
class CategoryProduct extends Component{ public ?int $category = null; public ?int $product = null; public Collection $categories; public Collection $products; public function mount(): void { $this->categories = Category::all(); $this->products = collect(); } public function updatedCategory($value): void { $this->products = Product::where('category_id', $value)->get(); $this->product = $this->products->first()->id ?? null; } public function render(): View { return view('livewire.category-product'); }}
Here we add a new method updatedCategory
. This will run after the property called category
is updated and sends its value. All that's left to do is get the products list and set the first product to be selected.
All that is left is to implement the Submit
button action with usual logic like validation, creating a record, redirecting, etc.
use App\Models\Order; class CategoryProduct extends Component{ // ... public function submit() { // Do validation Order::create(['product_id' => $this->product]); // Other related things for the creation process and redirect } // ...}
3-level Dropdown
For the third selection, we will add...