Blade for livewire component:
<div>
<input type="checkbox" class="form-checkbox" wire:model="collapsed" wire:click="toggleCollapsed">
@if ($collapsed)
show icon for collapsed state
@else
show icon for expanded state
@endif
</div>
Works as expected – $collapsed changed because of checkbox, appropriate icon shown.
But usage of checkbox for toggle visual state is unlogical.
Try to use more logical button:
<div>
<button wire:model="collapsed" wire:click="toggleCollapsed">
@if ($collapsed)
show icon for collapsed state
@else
show icon for expanded state
@endif
</button>
</div>
Don’t work.
First thought – button is not toggler itself, so I should inform, that collapse state changed:
public function toggleCollapsed(): void
{
$this->collapsed = !$this->collapsed;
}
Don’t work again.
Moreover this->collapsed
every time I entered into toggleCollapsed() has initial value – handy toggle just ignored.
Guess I should inform component after toggling $this->collapsed = !$this->collapsed
that component state changed (I see it is not the same as component’s public bool $collapsed
property). How to do that?
P.S. I’m strongly against of usage wire:click="$toggle('collapsed')"
because toggleCollapsed()
contains more complex logic not shown here.
2
Answers
The issue you’re facing is due to the fact that the
wire:model
directive is primarily used for input fields (like checkboxes) and it’s not applicable to elements like buttons. That’s why the checkbox works but the button doesn’t toggle the collapsed state properly.Here’s a step-by-step guide on how to handle the toggle using the button without the need for
wire:model
:Remove
wire:model
from the buttonSince
wire:model
is not suitable for buttons, remove it from the button. The button’s role here is just to trigger the toggle, andwire:click
will handle that.Update the Blade Template
Your updated Blade template should look like this:
toggleCollapsed()
Method WorksThe method you already wrote for toggling the collapsed state is correct:
This method should be fine as long as
$collapsed
is a public property of your Livewire component.Ensure that the collapsed property is declared as a public property in your Livewire component:
wire:model
Doesn’t Work with ButtonsThe
wire:model
directive is used to bind properties to form inputs (e.g., text fields, checkboxes) to synchronize the UI with the backend state in real-time. Since a button doesn’t have a "value" or a state like a checkbox, wire:model is ineffective here.By removing
wire:model
and relying onwire:click
to manually toggle the$collapsed
state, your button will work as expected. This method gives you the control you need to handle more complex logic inside the toggleCollapsed() function.Livewire 3 comes bundled with AlpineJs which is more useful in this case as the state of the menu can be entirely controlled client-side and then entangled into a Livewire property like the following example from the Livewire Docs:
As we can see in this snippet we can define the state of the menu,
open
in an outer div, toggling state on the click of the button and closing when clicking away.In the above example we entangle the state of the client side
open
with the Livewire propertyshowDropdown
, or in your case$collapsed
. We can then hook into the property update and execute any other logic around this.