I found a way to do what I described. Unfortunately, the code does not work 100%. The first "row" works as expected, but later added rows do not "connect" to livewire -> I am setting the wire:model attribute on the inputs but those are not working as expected, maybe someone knows how to fix that.
In my livewire component:
<x-form::form.elements.repeater id="contact-repeater" :required="true" group-name="contactPersons">
<div class="form-group row mb-3">
<div class="col-md-4">
<x-form::inputs.input
name="name"
wire:model.defer="name"
:required="true"
placeholder="Name"
/>
</div>
<div class="col-md-4">
<x-form::inputs.input
name="name"
wire:model="email"
:required="true"
type="email"
placeholder="E-Mail"
/>
</div>
<div class="col-md-4 d-flex align-items-center">
<a href="javascript:;" data-repeater-delete
class="btn btn-sm btn-light-danger">
<i class="ki-duotone ki-trash fs-5"><span class="path1"></span><span
class="path2"></span><span class="path3"></span><span class="path4"></span><span
class="path5"></span></i>
{{ __('basic::general.delete') }}
</a>
</div>
</div>
</x-form::form.elements.repeater>
The components view:
<div id="{{ $id }}" class="fv-row mb-10">
<x-form::general.label :id="$id" :help-text="$helpText" :label="$label" :required="$required"/>
<!--begin::Form group-->
<div class="form-group" wire:ignore>
<div data-repeater-list="{{ $groupName }}">
<div data-repeater-item>
{{ $slot }}
</div>
</div>
</div>
<!--end::Form group-->
<!--begin::Form group-->
<div class="form-group mt-5">
<a href="javascript:;" data-repeater-create class="btn btn-light-primary">
<i class="ki-duotone ki-plus fs-3"></i>
{{ __('basic::general.add') }}
</a>
</div>
<!--end::Form group-->
</div>
<script>
function initRepeater() {
let repeater = $('#{{ $id }}');
if (repeater.data('repeater-initialized')) {
return;
}
let setLivewireModel = function (element, name) {
// Find the index of the current item
let index = element.closest('[data-repeater-item]').index();
// Set the name
name = '{{ $groupName }}.' + index + '.' + name;
// Find the wire:model attribute name
// We can't just use wire:model because it might be wire:model.lazy or wire:model.defer
let attributeName;
let attributes = element[0].attributes;
for (let i = 0; i < attributes.length; i++) {
let currentAttrName = attributes[i].name;
if (currentAttrName.startsWith('wire:model')) {
attributeName = currentAttrName;
}
}
// Set the wire:model attribute name
element.attr(attributeName, name);
};
$('#{{ $id }}').repeater({
initEmpty: false,
isFirstItemUndeletable: {{ $required ? 'true' : 'false' }},
defaultValues: @json($defaultValues, JSON_FORCE_OBJECT),
show: function () {
$(this).slideDown();
{{ $showScript ?? '' }}
$(this).find('[livewire-input]').each(function () {
setLivewireModel($(this), $(this).attr('livewire-input'));
});
},
hide: function (deleteElement) {
$(this).slideUp(deleteElement);
{{ $hideScript ?? '' }}
}
});
repeater.find('[livewire-input]').each(function () {
setLivewireModel($(this), $(this).attr('livewire-input'));
});
repeater.data('repeater-initialized', true);
}
document.addEventListener("DOMContentLoaded", () => {
initRepeater();
Livewire.hook('message.processed', () => {
initRepeater();
});
});
</script>
The component class does not contain any specific code.
Maybe someone can help me. Just to sum up the problem:
- I can enter values in the first row (which exists on page load) -> data is added on livewire side
- I add a new row with the "add" button (uses jquery.repeater)
- Newly added inputs don't send data to livewire backend -> wire:model is correctly set.
Best regards