Search code examples
phplaravelmultidimensional-arraylaravel-livewire

Nesting Livewire components resetting values on update


I am trying to update data on a Livewire component in nested arrays, but they are not functioning as I would have hoped. I am trying to leverage the use of multidimensional arrays to manage the data of several inputs, as seen below.

@foreach ($items as $item)
   <input wire:model="items.{{ $item->id }}.employee_id" type="text">
   <input wire:model="items.{{ $item->id }}.name" type="text">
@endforeach

I am running into problems when typing data into these inputs. It will update one value (such as items.3.name) and reset all other data in the multidimensional array back to their original values.

I have tried changing the rules on the page after reading that Livewire requires them, but that hasn't helped. Here is a snippet of my controller:

public $items = [];
protected $rules = [
    'items.*.employee_id' => 'required',
    'items.*.name' => 'required',
];

public function mount() {
    $this->items = Employees::all()->toArray();
}

public function render() {
    return view('livewire.employees');
}

public function updatedName() {
    dd($this->items);
}

As far as I can tell, this should be working. What am I missing?


Solution

  • If $items is an array, you should know that the array index begins in 0. So, when you code this in the blade

    <input wire:model="items.{{ $item->id }}.employee_id" type="text">
    

    the $item->id value could be eg. 200, but the array doesn't have an index 200, then you are looping over array index, and not over model id values

    When you reference this:

    $this->items = User::all()->toArray();
    

    the output is something like this:

    array: 3 [
     0 => array:6[
       "id" => 34
       "name" => "First Name"
       "email" => "firstname@email.com"
       //...
     ]
     1 => array:6[
       "id" => 47
       "name" => "Second Name"
       "email" => "secondname@email.com"
       //...
     ]
     2 => array:6[] 
    ]
    

    I think the correct way is to loop like:

    @foreach($items as $key => $item)
      <input wire:model="items.{{ $key }}.name" type="text">
      <input wire:model="items.{{ $key }}.email" type="email">
    @endforeach
    

    and of course, like Qirel said, you should wrap it in a root element and add the wire:key directive

    @foreach($items as $key => $item)
      <div wire:key="input-group-{{ $key }}">
        <input wire:model="items.{{ $key }}.name" type="text">
        <input wire:model="items.{{ $key }}.email" type="email">
      </div>
    @endforeach