Courses

Practical Livewire 3: Order Management System Step-by-Step

Category Edit - Inline in the Table

Now we will make an editing form. But, differently from the Create form, this time we will make inline editing, so you could edit the category inside the table, without leaving the page.

edit category form

First, we need a public property to know which category will be edited.

app/Http/Livewire/CategoriesList.php:

class CategoriesList extends Component
{
// ...
 
public int $editedCategoryId = 0;
}

Next, we will add the wire:click action to the Edit button which will receive Category ID and in the component will be set to $editedCategoryId.

resources/livewire/categories-list.blade.php:

<x-primary-button>
<x-primary-button wire:click="editCategory({{ $category->id }})">
Edit
</x-primary-button>

app/Livewire/CategoriesList.php:

class CategoriesList extends Component
{
// ...
 
public function editCategory(int $categoryId): void
{
$this->editedCategoryId = $categoryId;
 
$this->category = Category::find($categoryId);
$this->name = $this->category->name;
$this->slug = $this->category->slug;
}
 
// ...
}

Now, for the form, when $editedCategoryId is set, we need to show the inputs, otherwise we show the values as text. For that, we will play with the CSS class hidden and some if-else statements.

resources/livewire/categories-list.blade.php:

//
<td class="px-6 py-4 text-sm leading-5 text-gray-900 whitespace-no-wrap">
{{ $category->name }}
</td>
<td class="px-6 py-4 text-sm leading-5 text-gray-900 whitespace-no-wrap">
{{ $category->slug }}
</td>
 
{{-- Inline Edit Start --}}
<td class="@if($editedCategoryId !== $category->id) hidden @endif px-6 py-4 text-sm leading-5 text-gray-900 whitespace-no-wrap">
<x-text-input wire:model.live.debounce="name" id="name" class="py-2 pr-4 pl-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
</td>
<td class="@if($editedCategoryId !== $category->id) hidden @endif px-6 py-4 text-sm leading-5 text-gray-900 whitespace-no-wrap">
<x-text-input wire:model="slug" id="slug" class="py-2 pr-4 pl-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
</td>
{{-- Inline Edit End --}}
 
{{-- Show Category Name/Slug Start --}}
<td class="@if($editedCategoryId === $category->id) hidden @endif px-6 py-4 text-sm leading-5 text-gray-900 whitespace-no-wrap">
{{ $category->name }}
</td>
<td class="@if($editedCategoryId === $category->id) hidden @endif px-6 py-4 text-sm leading-5 text-gray-900 whitespace-no-wrap">
{{ $category->slug }}
</td>
{{-- Show Category Name/Slug End --}}
 
//

For buttons, it's very similar, when editing we need to show the Save and Cancel buttons, otherwise Edit and Delete.

resources/livewire/categories-list.blade.php:

@if($editedCategoryId === $category->id)
<x-primary-button wire:click="save">
Save
</x-primary-button>
<x-primary-button wire:click.prevent="cancelCategoryEdit">
Cancel
</x-primary-button>
@else
<x-primary-button wire:click="editCategory({{ $category->id }})">
Edit
</x-primary-button>
<button class="px-4 py-2 text-xs text-red-500 uppercase bg-red-200 rounded-md border border-transparent hover:text-red-700 hover:bg-red-300">
Delete
</button>
@endif

edit category form

As you can see, when saving, we use the same method save(). When creating a new category, we must check if the $category property is null. If it is null, then a new category will be created; otherwise updated.

When saving we need to check if $editedCategoryId is set, if it is we don't need to set position, and after saving reset $editedCategoryId.

For cancel, we add a new method cancelCategoryEdit() where we just need to reset $editedCategoryId and error bag.

app/Livewire/CategoriesList.php:

class CategoriesList extends Component
{
// ...
 
public function save()
{
$this->validate();
 
if (is_null($this->category)) {
$position = Category::max('position') + 1;
Category::create(array_merge($this->only('name', 'slug'), ['position' => $position]));
} else {
$this->category->update($this->only('name', 'slug'));
}
 
$this->reset('showModal');
$this->resetValidation();
$this->reset('showModal', 'editedCategoryId');
}
 
public function cancelCategoryEdit()
{
$this->resetValidation();
$this->reset('editedCategoryId');
}

After we click Save, the category data is updated both in the DB and in the table visually.

category after edit

Previous: Categories Reorder with Drag-Drop
avatar

I think that you need to clear the validation messages when you click "Edit" on a different table row, than the one you are editing. Example (gif animation): https://postimg.cc/nMXX50X6

👍 2
avatar

@phuyer, is cancelCategoryEdit button working for you?

avatar

Good point for the validation, updated this lesson and repo.

avatar
You can use Markdown
avatar

The cancelCategoryEdit button is not working. Also validation bug still presists as mentioned by @phuyer!

avatar

What's not working for you? As you saw in phuyer animation, the buttons works for him. Validation fixed now.

avatar

I've had issue with cancel button as well. If I add prevent the click event, it cancels

 <x-primary-button wire:click.prevent="cancelCategoryEdit()">
	 Cancel
</x-primary-button>
avatar

Providing each 'x-primary-button' with a unique id works for me.

avatar

Same here cancel not working.

avatar

for the validation bug reset the validation in the editCategory function :-

	public function editCategory($categoryId){
    $this->resetValidation();
    $this->editedCategoryId = $categoryId;
    $this->category = Category::find($categoryId);
}
avatar

@Filimoni Lutunaika I changed that x-primary-button to span tag and that worked

avatar

Use hidden class technique just like before. Like this

<td class="@if ($editedCategoryId !== $category->id) hidden @endif px-6 py-4 text-sm leading-5 text-gray-900 whitespace-no-wrap">
		<x-button wire:click="save">
				Save
		</x-button>
		<x-button wire:click.prevent="cancelCategoryEdit">
				Cancel
		</x-button>
</td>
<td class="@if ($editedCategoryId === $category->id) hidden @endif px-6 py-4 text-sm leading-5 text-gray-900 whitespace-no-wrap">

		<x-button wire:click="editCategory({{ $category->id }})">
				Edit
		</x-button>
		<button class="px-4 py-2 text-xs text-red-500 uppercase bg-red-200 rounded-md border border-transparent hover:text-red-700 hover:bg-red-300">
				Delete
		</button>
</td>
avatar

For classes, at least for me, it's better to use @class() Blade directive

avatar
You can use Markdown
avatar

Could someone point out the direction to go if I want to use the create modal for editing instead of this inline editing?

avatar

Toggle $showModal on editCategory component's method (which gets called when edit button is clicked) and for end-user's readability change the inner text in the modal's button to "Update" instead of "Create", and the modal title to "Edit Category" when $category (or $editedCategoryId, after all one doesn't exists without the other) doesn't have the default "empty" value (for $category, null).

Also make sure to populate the form fields with the category data, and add a closeModal method to reset everything when the modal is closed.

avatar
You can use Markdown
avatar
You can use Markdown