I have a Test model, with the JSON attribute inputs
, which is a flat array of strings (e.g. ['foo', 'bar', 'baz']
).
I'm trying to bind this field up to Livewire so that I can have a component that allows the user to add, modify and delete elements from this array. I'm able to get the following working, where each input is automatically populated using the array:
To create this, I use the following code (
<div class="test">
...
@php $index = 0; @endphp
@foreach ($this->test->inputs as $input)
@php $index ++; @endphp
<div class="input-wrap">
<label>{{ $index }}</label>
<input type="text" placeholder="input..." value="{{ $input }}">
<i class="fas fa-times"></i>
</div>
@endforeach
...
</div>
However, I'm struggling to work out the best way of binding these fields using livewire so they are synced with the backend. I thought about binding a hidden input field with the raw json in, with some frontend JS to listen to changes and keep updated, but when it comes to adding a new element this will mean duplicating the .input-wrap
div on the front- and backend, which feels like a code-smell and would be trickier to maintain.
Is there a way of achieving this without relying heavily on front-end JS for the rendering logic?
While this suggested similar question was helpful in getting me to the answer, it didn't quite tackle what I was after. To solve this, I added the following methods to the component's class:
public function addInput()
{
$this->test->inputs->append('');
$this->test->save();
}
public function removeInput($index)
{
$this->test->inputs->offsetUnset($index);
$this->test->save();
}
I also added 'test.inputs.*' => 'string'
to its $rules, and to allow direct modification of the array I casted the inputs to an array object using 'inputs' => AsArrayObject::class
in the model definition.
Then, on the frontend:
@foreach ($this->test->inputs as $input)
<div class="input-wrap">
<label>{{ $index + 1 }}</label>
<input wire:model="test.inputs.{{ $index }}">
<i wire:click="removeInput({{ $index }})" class="fas fa-times"></i>
</div>
@php $index ++; @endphp
@endforeach