danichangt's avatar

Error udpate a model in Livewire

I have a array model thath trigger a funcion to update a variable. I have an array of checkboxes in a table to add a bonifications in a variable. I use wire:model='bono' to my livewire variable $bono = [].

<tr>
                                                    <td class="py-2 px-4 items-center">
                                                        <div class="relative flex gap-x-20">
                                                            <div class="flex h-6 items-center">
                                                                <input wire:model='bono' wire:change='actualizarBonos'
                                                                    type="checkbox" id="bono-{{ $bn->id }}"
                                                                    value="{{ $bn->id }}"
                                                                    class="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-600">
                                                            </div>
                                                            <div class="text-sm leading-6">

And i have a funcion that verify if the array contains two bonifications at the same time, if that's true, I want to delete the bonification with id 10, but when in the table of checkboxes mark the id=10 bonificacion first and the id=4 bonificacion after, the function unset the bonificacion with id=10 but the variable don't update and still marked on the table.

this is my function code

		
} elseif ($renglon->renglon == '021') {
                if (!empty($this->bono)) {
                    if (in_array(4, $this->bono) && in_array(10, $this->bono)) {
                        $index = array_search(10, $this->bono);
                        if ($index !== false) {
                            unset($this->bono[$index]);
                        }
                    }
                }

                $bonos = Bonificacion::select('bono', 'cantidad')->whereIn('id', $this->bono)->get();
                if (!empty($bonos)) {
                    foreach ($bonos as $bono) {
                        if ($bono->bono == 'Bono profesional' || $bono->bono == 'Bono de antigüedad') {
                            $this->aguinaldo += number_format($bono->cantidad, 2, '.', '');
                        }
                    }
                }
            }

Someone can help me? Thank you so much.

0 likes
24 replies
danichangt's avatar

@vincent15000 that my entire table code

<table class="min-w-full bg-white rounded-lg overflow-hidden text-center ring-1 ring-gray-300">
                                        <thead class="bg-gray-100 text-center">
                                            <tr>
                                                <th class="w-1/12 py-2 px-4">Cantidad</th>
                                                <th class="w-1/12 py-2 px-4">Bono</th>
                                                <th class="w-1/4 py-2 px-4">Renglón</th>
                                            </tr>
                                        </thead>
                                        <tbody>
                                            @foreach ($bonos as $bn)
                                                <tr>
                                                    <td class="py-2 px-4 items-center">
                                                        <div class="relative flex gap-x-20">
                                                            <div class="flex h-6 items-center">
                                                                <input wire:model='bono' wire:change='actualizarBonos'
                                                                    type="checkbox" id="bono-{{ $bn->id }}"
                                                                    value="{{ $bn->id }}"
                                                                    class="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring indigo-600">
                                                            </div>
                                                            <div class="text-sm leading-6">
                                                                <label for="bono-{{ $bn->id }}">
                                                                    @if ($bn->bono == 'Bono por disponibilidad y riesgo')
                                                                        @if ($salario != '')
                                                                            Q
                                                                            {{ number_format($salario * $bn->cantidad, 2, '.', '') }}
                                                                        @else
                                                                            Q {{ $bn->cantidad }}
                                                                        @endif
                                                                    @elseif($bn->bono == 'Aguinaldo')
                                                                        @if ($aguinaldo != '')
                                                                            Q
                                                                            {{ number_format($aguinaldo, 2, '.', '') }}
                                                                        @else
                                                                            Q {{ $bn->cantidad }}
                                                                        @endif
                                                                    @else
                                                                        Q {{ $bn->cantidad }}
                                                                    @endif
                                                                </label>
                                                            </div>
                                                        </div>
                                                    </td>
                                                    <td class="py-2 px-4">{{ $bn->bono }}</td>
                                                    <td class="py-2 px-4">{{ $bn->renglon }} - {{ $bn->nombre }}
                                                    </td>
                                                </tr>
                                            @endforeach
                                        </tbody>
                                    </table>
1 like
vincent15000's avatar

@danichangt Yes that's the case, you need to add a key to each row inside the loop.

@foreach ($bonos as $bn)
	<tr wire:key="{{ 'bonification-'.$nb->id }}">
		...
	</tr>
@endforeach

And if it doesn't work better, add also a key to each checkbox.

<input type="checkbox" ... wire:key="{{ 'bonification-checkbox-'.$nb->id }}" />
danichangt's avatar

@vincent15000 i updated the code

<table
                                        class="min-w-full bg-white rounded-lg overflow-hidden text-center ring-1 ring-gray-300">
                                        <thead class="bg-gray-100 text-center">
                                            <tr>
                                                <th class="w-1/12 py-2 px-4">Cantidad</th>
                                                <th class="w-1/12 py-2 px-4">Bono</th>
                                                <th class="w-1/4 py-2 px-4">Renglón</th>
                                            </tr>
                                        </thead>
                                        <tbody>
                                            @foreach ($bonos as $bn)
                                                <tr wire:key='{{ 'bono.$bono->id' }}'>
                                                    <td class="py-2 px-4 items-center">
                                                        <div class="relative flex gap-x-20">
                                                            <div class="flex h-6 items-center">
                                                                <input wire:model='bono'
                                                                    wire:key='{{ 'bono_chk.$bono->id' }}'
                                                                    wire:change='actualizarBonos' type="checkbox"
                                                                    id="bono-{{ $bn->id }}"
                                                                    value="{{ $bn->id }}"
                                                                    class="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-600">
                                                            </div>
                                                            <div class="text-sm leading-6">
                                                                <label for="bono-{{ $bn->id }}">
                                                                    @if ($bn->bono == 'Bono por disponibilidad y riesgo')
                                                                        @if ($salario != '')
                                                                            Q
                                                                            {{ number_format($salario * $bn->cantidad, 2, '.', '') }}
                                                                        @else
                                                                            Q {{ $bn->cantidad }}
                                                                        @endif
                                                                    @elseif($bn->bono == 'Aguinaldo')
                                                                        @if ($aguinaldo != '')
                                                                            Q
                                                                            {{ number_format($aguinaldo, 2, '.', '') }}
                                                                        @else
                                                                            Q {{ $bn->cantidad }}
                                                                        @endif
                                                                    @else
                                                                        Q {{ $bn->cantidad }}
                                                                    @endif
                                                                </label>
                                                            </div>
                                                        </div>
                                                    </td>
                                                    <td class="py-2 px-4">{{ $bn->bono }}</td>
                                                    <td class="py-2 px-4">{{ $bn->renglon }} - {{ $bn->nombre }}
                                                    </td>
                                                </tr>
                                            @endforeach
                                        </tbody>
                                    </table>

but the problem still persists

1 like
vincent15000's avatar

@danichangt

Assuming $bono->id = 10.

This can't work because it generates always the same key, {{ 'bono.$bono->id' }} gives the string bono$bono->id.

This generates different keys because the id is a variable : {{ 'bono'.$bono->id }} gives the string bono.10.

danichangt's avatar

@vincent15000 yes I updated the code, now the wire:key wire:key='bono{{ $bn->id }}' and wire:key='bono_chk{{ $bn->id }}', but I still have the problme I don'tn why.

I was makeing tests and the variable $this->bono change of type automaticaly when y mark the bonification with id 10, I mark the bonification id 4, unmark bonificacion id 4, but when unmark, the variable change from array to boolean type.

1 like
danichangt's avatar

@vincent15000 public $bono = []. I don't know why the variable change the type. I added some dump() to see how the code works and when mark the bonus with id 10, the code work fine, so I mark the bonus with id 4, but when I unmark the bonus with id 10, the bonus with id 4 get unmarked automatically and $bonus = [] turn boolean automatically too.

elseif ($renglon->renglon == '021') {
                dump('inicio_renglon_no_vacio: ' . gettype($this->bono));
                if (!empty($this->bono)) {
                    dump('inicio_bono_no_vacio: ' . gettype($this->bono));
                    dump($this->bono);
                    if (in_array(4, $this->bono) && in_array(10, $this->bono)) {
                        dump('validacion');
                        dump($this->bono);

                        $index = array_search(10, $this->bono);
                        dump('indice');
                        dump($index);
                        if ($index !== false) {
                            unset($this->bono[$index]);
                            dump('unset');
                            dump($this->bono);
                        }
                        dump('fin_doble_validacion: ' . gettype($this->bono));
                    }
                    dump('fin_validacion: ' . gettype($this->bono));
                }
                dump('fin_validacion: ' . gettype($this->bono));
                if (is_array($this->bono)) {
                    $bonos = Bonificacion::select('bono', 'cantidad')->whereIn('id', $this->bono)->get();
                }
                dump('otro: ' . gettype($this->bono));
                if (!empty($bonos)) {
                    foreach ($bonos as $bono) {
                        if ($bono->bono == 'Bono profesional' || $bono->bono == 'Bono de antigüedad') {
                            $this->aguinaldo += number_format($bono->cantidad, 2, '.', '');
                        }
                    }
                }
                dump('otro: ' . gettype($this->bono));
            }

dump() with bonus id=10 checked

"inicio_renglon_no_vacio: array" // app\Livewire\Puesto\Puestos.php:275
"inicio_bono_no_vacio: array" // app\Livewire\Puesto\Puestos.php:277
array:1 [▼ // app\Livewire\Puesto\Puestos.php:278
  0 => "10"
]
"fin_validacion: array" // app\Livewire\Puesto\Puestos.php:293
"fin_validacion: array" // app\Livewire\Puesto\Puestos.php:295
"otro: array" // app\Livewire\Puesto\Puestos.php:299
"otro: array" // app\Livewire\Puesto\Puestos.php:307

dump() with bonus with id=10 and id=4 checked

"inicio_renglon_no_vacio: array" // app\Livewire\Puesto\Puestos.php:275
"inicio_bono_no_vacio: array" // app\Livewire\Puesto\Puestos.php:277
array:2 [▼ // app\Livewire\Puesto\Puestos.php:278
  0 => "10"
  1 => "4"
]
"validacion" // app\Livewire\Puesto\Puestos.php:280
array:2 [▼ // app\Livewire\Puesto\Puestos.php:281
  0 => "10"
  1 => "4"
]
"indice" // app\Livewire\Puesto\Puestos.php:284
0 // app\Livewire\Puesto\Puestos.php:285
"unset" // app\Livewire\Puesto\Puestos.php:288
array:1 [▼ // app\Livewire\Puesto\Puestos.php:289
  1 => "4"
]
"fin_doble_validacion: array" // app\Livewire\Puesto\Puestos.php:291
"fin_validacion: array" // app\Livewire\Puesto\Puestos.php:293
"fin_validacion: array" // app\Livewire\Puesto\Puestos.php:295
"otro: array" // app\Livewire\Puesto\Puestos.php:299
"otro: array" // app\Livewire\Puesto\Puestos.php:307

dump() with bonus id=10 unchecked and the id=4 will unchecked automatically too

"inicio_renglon_no_vacio: boolean" // app\Livewire\Puesto\Puestos.php:275
"inicio_bono_no_vacio: boolean" // app\Livewire\Puesto\Puestos.php:277
true // app\Livewire\Puesto\Puestos.php:278
1 like
vincent15000's avatar

@danichangt Have you tried to specify an index inside the array ?

<input type="checkbox" wire:model="bono.{{ $loop->index }}" />
danichangt's avatar

I can fix it. Just added a reindex method after unset():

unset($this->bono[$index]);
$this->bono = array_values($this->bono);
1 like
vincent15000's avatar

@danichangt Why do you need unset($this->bono[$index]); ? That's really not a good idea to manage the checboxes states.

vincent15000's avatar

@danichangt Here is an example of code that works fine.

public $activities;
public $selectedActivityIds = [];

public function mount()
{
    $this->activities = Activity::all();
}

public function render()
{
    return view('livewire.form');
}
<div>
    @foreach ($activities as $activity)
        <input type="checkbox" id="{{ 'activity-'.$activity->id }}" value="{{ $activity->id }}" wire:model="selectedActivityIds" wire:key="{{ 'activity-'.$activity->id }}" />
        <label for="{{ 'activity-'.$activity->id }}">{{ $activity->name }}</label>
    @endforeach

    {{ json_encode($selectedActivityIds) }}
</div>
1 like
danichangt's avatar

@vincent15000 To the situation I have. I coding for a jobs that have several bonus. For the jobs in the 'category 021' can't have two bonus at the same time (specifically bonus with id=10 y id=4) I tell you with ids but I coding in Spanish haha. So, when the user check both bonus, I want to uncheck the bonus with id=10. For that case I chose use unset() to delete or remove that bonus from de array. If you know how I can do this with a better practice, I will be thankful with you. Thank you.

1 like
danichangt's avatar

@vincent15000 thats a really good question haha. The checkboxes was I got on mind first, but, can you tell me what's the difference? Thank you.

1 like
vincent15000's avatar

@danichangt Oh you don't now the difference between checkboxes and radios buttons ?

Hmmm ... how about learning input types ?

danichangt's avatar

@vincent15000 Sorry, but there's a several bonus in the options for the job, id=4 and id=10 are not the ones, but they never be together in the array. Btw, you're right, I have (need) still learn a huge part of coding.

1 like
vincent15000's avatar

@danichangt You seem to say that the user can select only one bonus amont all bonuses : in this case, a radio button or a select/options field are better appropriated.

danichangt's avatar

@vincent15000 The user can select zero or many bonus. For example, for the jobs in the category '021' I have 8 bonus avilable, so I can select 7 of 8 bonus because the bonus id=4 and the bonus=10 can't be together. I don't why but that's the way the bonus' jobs works in my country.

1 like
vincent15000's avatar

@danichangt What is strange is that you have to filter the bonuses according to their ids. In this case wouldn't it be better to not use the database for the bonuses and instead use a simple PHP enum ? And then you could fix the rules about how a user can select or not the bonuses.

If a user can select several bonuses, then radio buttons aren't possible.

Snapey's avatar

The easiest way to create wire key

wire:key="bono-{{ $bono->id }}"

note that the concatenation is done in html not php. Never use $loop->index for wire key!

The easiest way to deal with checkboxes in livewire is

wire:model="bono.{{$bono->id}}"

The easiest way to see what is going off in livewire properties is to use debugbar

2 likes
vincent15000's avatar

@Snapey Why never use $loop->index for wire key ? What's important is to use a unique prefix.

Oh yes I understand, if the list is about to evolve dynamically, it can generate bugs.

Snapey's avatar

@vincent15000 because on first render, the key will be 1,2,3,4,5 etc. On the second render the key will be 1,2,3,4,5

How does the dom morphng know which row was removed?

With an id that relates to the record, on first render, the key will be 1,2,3,4,5 etc. On the second render the key will be 1,2,4,5,6 or even 5,4,3,2,1 if the ordering was changed.

1 like
vincent15000's avatar

@Snapey Yes, exactly. On the second render, the index 1 will not necessarily be the same as on the first render, so it's important that it has a different key.

Please or to participate in this conversation.