Courses

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

Categories Table - Toggle Active

You probably have noticed that we have "Active" toggle in the table. Let's implement its behavior, so you could click on that Toggle to change the value immediately, without leaving the table page.

This is the expected result, visually:

Better toggle button

You might have seen that in Categories migration we have a column is_active, so we will change its value in the database.

First, we need a public property in the Livewire Component. It will be an array and called $active.

app/Livewire/CategoriesList.php:

class CategoriesList extends Component
{
use WithPagination;
 
public Category $category;
 
public bool $showModal = false;
 
public array $active = [];
//
}

Next, we need a list of active categories. For this, we will use a Collections method mapWithKeys().

class CategoriesList extends Component
{
//
public function render(): View
{
$categories = Category::paginate(10);
 
$this->active = $categories->mapWithKeys(
fn (Category $item) => [$item['id'] => (bool) $item['is_active']]
)->toArray();
 
return view('livewire.categories-list', [
'categories' => $categories,
]);
}
//
}

This will return the array result of category_id => true/false:

array:10 [▼ // app/Http/Livewire/CategoriesList.php:50
1 => true
2 => true
3 => true
4 => true
5 => true
6 => true
7 => true
8 => true
9 => true
10 => true
]

Now we can bind the toggle button to the active property and add action to the toggle activity, with this code:

wire:model="active.{{ $category->id }}"
wire:click="toggleIsActive({{ $category->id }})"

The full code of the <td> column:

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

<td class="px-6">
<div class="inline-block relative mr-2 w-10 align-middle transition duration-200 ease-in select-none">
<input type="checkbox" name="toggle" class="block absolute w-6 h-6 bg-white rounded-full border-4 appearance-none cursor-pointer focus:outline-none toggle-checkbox" />
<label for="toggle" class="block overflow-hidden h-6 bg-gray-300 rounded-full cursor-pointer toggle-label"></label>
<input wire:model="active.{{ $category->id }}" wire:click="toggleIsActive({{ $category->id }})" type="checkbox" name="toggle" id="{{ $loop->index.$category->id }}" class="block absolute w-6 h-6 bg-white rounded-full border-4 appearance-none cursor-pointer focus:outline-none toggle-checkbox" />
<label for="{{ $loop->index.$category->id }}" class="block overflow-hidden h-6 bg-gray-300 rounded-full cursor-pointer toggle-label"></label>
</div>
</td>

By default, all categories are active, so you should see all of them marked as active in the table:

active categories

To make a button look like a toggle button, we need to add some custom CSS.

resources/css/app.css:

@tailwind base;
@tailwind components;
@tailwind utilities;
 
.toggle-checkbox:checked {
@apply right-0 border-green-400;
}
 
.toggle-checkbox:checked + .toggle-label {
@apply bg-green-400;
}

Better toggle button

Next, we will make the button work. In the Blade file, we called the action toggleIsActive which receives category ID.

app/Livewire/CategoriesList.php:

class CategoriesList extends Component
{
// ...
 
public function toggleIsActive(int $categoryId): void
{
Category::where('id', $categoryId)->update([
'is_active' => $this->active[$categoryId],
]);
}
 
// ...

This method is very simple, we just need to update Category with the value from the $active property, calling the specific element.

That's it, our active/inactive Toggle button works!

Previous: Create New Category Modal
avatar

How can we apply a newer @checked blade directive?

avatar

I don't think we can apply it here.

avatar
You can use Markdown
avatar

Getting Error

Attempt to read property "index" on null

I thank something is missing, but What, and where should it be?

avatar
You can use Markdown
avatar

Why are we creating an in-memory dictionary (an associative array with same structure for all key-value pairs) to hold the value from each category's is_active property?

Couldn't we just bind the toggling to the is_active of the current iterated $category inside the loop?

I did that and got the same result, with more performance and readability: the active array does not need to be recreated on every update in $categories; the toggleIsActive receives as argument the $category itself rather than its id (thus no need to fetch it in the database: we just update it directly).

e.g:

    public function activeToggle(Category $category)
    {
        $category->update(['is_active' => !$category->is_active]);
    }
<input
      type="checkbox"
      id="is-active-{{ $category->id }}"
      wire:key="is-active-{{ $category->id }}"
      wire:click="activeToggle({{ $category }})"
      wire:loading.attr="disabled"
      {{ $category->is_active ? 'checked' : '' }}
      class="sr-only peer"/>
avatar
You can use Markdown
avatar
You can use Markdown