Now let's create a real dynamic thing with Livewire: a Modal to show the form to create a category.
Also, we will make the Slug field automatically generated after we change the Name field.
First, we will add the modal itself with form. In the resources/livewire/categories-list.blade.php
just before last </div>
, add this code:
// <div class="@if (!$showModal) hidden @endif flex items-center justify-center fixed left-0 bottom-0 w-full h-full bg-gray-800 bg-opacity-90"> <div class="w-1/2 bg-white rounded-lg"> <form wire:submit.prevent="save" class="w-full"> <div class="flex flex-col items-start p-4"> <div class="flex items-center pb-4 mb-4 w-full border-b"> <div class="text-lg font-medium text-gray-900">Create Category</div> <svg wire:click.prevent="$set('showModal', false)" class="ml-auto w-6 h-6 text-gray-700 cursor-pointer fill-current" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 18"> <path d="M14.53 4.53l-1.06-1.06L9 7.94 4.53 3.47 3.47 4.53 7.94 9l-4.47 4.47 1.06 1.06L9 10.06l4.47 4.47 1.06-1.06L10.06 9z" /> </svg> </div> <div class="mb-2 w-full"> <label class="block text-sm font-medium text-gray-700" for="name"> Name </label> <input wire:model.live.debounce="name" id="name" class="py-2 pr-4 pl-2 mt-2 w-full text-sm rounded-lg border border-gray-400 sm:text-base focus:outline-none focus:border-blue-400" /> @error('name') <span class="text-sm text-red-500">{{ $message }}</span> @enderror </div> <div class="mb-2 w-full"> <label class="block text-sm font-medium text-gray-700" for="slug"> Slug </label> <input wire:model="slug" id="slug" class="py-2 pr-4 pl-2 mt-2 w-full text-sm rounded-lg border border-gray-400 sm:text-base focus:outline-none focus:border-blue-400" /> @error('slug') <span class="text-sm text-red-500">{{ $message }}</span> @enderror </div> <div class="mt-4 ml-auto"> <button class="px-4 py-2 font-bold text-white bg-blue-500 rounded hover:bg-blue-700" type="submit"> Create </button> <button wire:click="$set('showModal', false)" class="px-4 py-2 font-bold text-white bg-gray-500 rounded" type="button" data-dismiss="modal"> Close </button> </div> </div> </form> </div> </div> </div>
As you can see, in the modal we have the $showModal
variable and binded form inputs to name
and slug
. In the CategoriesList
component, we need to add public properties for them.
app/Livewire/CategoriesList.php:
class CategoriesList extends Component{ use WithPagination; public ?Category $category = null; public string $name = ''; public string $slug = ''; public bool $showModal = false; public function render(): View { $categories = Category::paginate(10); return view('livewire.categories-list', [ 'categories' => $categories, ]); }}
To open the modal, first, add the wire:click
action to the Add Category
button.
resources/livewire/categories-list.blade.php:
<x-primary-button wire:click="openModal" type="button" class="mb-4"> Add Category</x-primary-button>
When the button is clicked, the openModal
method in the Livewire component will be called. In this method, we set $showModal
to true.
app/Livewire/CategoriesList.php:
class CategoriesList extends Component{ use WithPagination; public Category $category; public bool $showModal = false; public function openModal(): void { $this->showModal = true; } public function render(): View { $categories = Category::paginate(10); return view('livewire.categories-list', [ 'categories' => $categories, ]); }}
Now, after clicking Add Category
, you will see the modal with the form.
Before creating a category, let's add validation to the form. For this, we will use the rules()
method in the Livewire component.
class CategoriesList extends Component{ use WithPagination; // ... protected function rules(): array { return [ 'name' => ['required', 'string', 'min:3'], 'slug' => ['nullable', 'string'], ]; } }
For the slug field, we will generate it automatically from the name. To achieve this, we will use Livewire Lifecycle Hooks.
app/Http/Livewire/CategoriesList.php:
use Illuminate\Support\Str; class CategoriesList extends Component{ use WithPagination; // ... public function updatedName(): void { $this->slug = Str::slug($this->name); } // ...}
Now, saving the category.
In the form we added a Livewire Action wire:submit="save"
, which means when the Create
button is pressed, the save()
method in Livewire Component will be called.
class CategoriesList extends Component{ // ... public function save() { $this->validate(); Category::create($this->only('name', 'slug')); $this->reset('showModal'); } }
Here, we first validate and then save the category to the DB. After that, we set the $showModal
to its initial value which is false
.
A new category is created successfully, yay!
thank you for this page
The add category button does not show modal on click in laravel 10.
Are there any errors in your browser console? No one else reported such bug.
There no errors on the browser console
Also need repo to see your code
Sorry for the necro... I got same problem with
Livewire: Multiple root elements detected.
The problem is that the modal's div was outside the main div, after moving it inside, everything worked as expected.
PS. I am using laravel 10
Its just how livewire works.
PS Don't forget to have a cmd with "npm run dev" running if you are on Laravel 10. That was the problem for me with the modal not showing.
About postions: One should create the postions in numerical order, else moving a post with drag and drop won't work properly. So, move the creation of categories from the Categoryseeder (delete it) into the Databaseseeder:
livewire.js?id=a27c4ca2:347 Uncaught TypeError: Cannot read properties of null (reading 'name') at livewire.js?id=a27c4ca2:347:19 at Array.reduce (
It seems that in the latest version of Livewire, the binding
category.name
is not working as it works in this course. struggling to find a solution.Its because you are using the old syntax. Properties can't be binded to model directly
How do I fix that?
Read the docs. You can enable the old syntax but it is not recommended. This course was made when v3 wasn't released.