When nested boolean property values (e.g. nested.value) of a Livewire component change, checkboxes do not change their checked state.
Here is a wirebox with the scenario: https://wirebox.app/b/x58lv Initial state is correct. When a model is loaded, the checked states do not change. The checkboxes with not nested values work.
This occurs for example when I use Livewire Form Objects. No problem with not nested values.
<?php
namespace App\Livewire;
use Livewire\Component;
use App\Models\BoolModel;
class Simple extends Component
{
public $bools = [
'one' => true,
'two' => false,
];
public string $name = 'none';
public bool $one = true;
public bool $two = false;
public function render()
{
return view('livewire.simple');
}
public function load($id)
{
$model = BoolModel::find($id);
$this->name = $model->name;
$this->bools = [
'one' => $model->one,
'two' => $model->two,
];
$this->one = $model->one;
$this->two = $model->two;
}
}
<div>
<h1 class="text-white">{{ $name }}</h1>
<div class="flex w-full h-full items-center justify-center text-gray-300 px-16">
<div class="text-light me-3">Array:</div>
<input type="checkbox" wire:model.blur="bools.one" /><span class="mx-2"> :: {{$bools['one'] ? 'true' : 'false'}}</span>
<input type="checkbox" wire:model.blur="bools.two" /><span class="mx-2"> :: {{$bools['two'] ? 'true' : 'false'}}</span>
</div>
<div class="flex w-full h-full items-center justify-center text-gray-300 px-16">
<div class="text-light me-3">single:</div>
<input type="checkbox" wire:model.blur="one" /><span class="mx-2"> :: {{$one ? 'true' : 'false'}}</span>
<input type="checkbox" wire:model.blur="two" /><span class="mx-2"> :: {{$two ? 'true' : 'false'}}</span>
</div>
<div>
<button class="bg-white" role="button" wire:click="load(1)">load 1</button>
<button class="bg-white" role="button" wire:click="load(2)">load 2</button>
</div>
</div>
Laravel Sushi (which you are using in your Wirebox example) converts booleans to integers by default as many DBMS engines do
To verify this behaviour you can replace the ternary operators in the <span> tags of the view with var_export() which output the real values of the variables:
<!--<span class="mx-2"> :: {{$bools['one'] ? 'true' : 'false'}}</span>-->
<span class="mx-2"> :: {{var_export($bools['one'], true)}}</span>
<!--<span class="mx-2"> :: {{$bools['two'] ? 'true' : 'false'}}</span>-->
<span class="mx-2"> :: {{var_export($bools['two'], true)}}</span>
<!--<span class="mx-2"> :: {{$one ? 'true' : 'false'}}</span>-->
<span class="mx-2"> :: {{var_export($one, true)}}</span>
<!--<span class="mx-2"> :: {{$two ? 'true' : 'false'}}</span>-->
<span class="mx-2"> :: {{var_export($two, true)}}</span>
When you assign the integer to non-array properties that are defined as bools in your class, e.g.: public bool $one = true;
, an implicit cast is performed and the numeric values are converted to booleans.
On the other hand this conversion is not performed automatically when these values are assigned to the items of the associative array
When Livewire assigns values to checkboxes it evidently checks the boolean values strictly and if it receives non-boolean values (such as 0 or 1) it cannot set the checks correctly
Solution:
To obtain the correct behaviour for the values which are elements of an associative array you can add casts in your model:
protected $casts = [
'one' => 'boolean',
'two' => 'boolean'
];
in this way the properties of the model will contain boolean values and the arrays will receive booleans too