Modify the state of a repeater when an item is added

In v2 I had set up a listener to listen on the repeater::createItem event, but I can see that the repeaters are no longer using events in v3. I tried adding something like
->addAction(fn (Action $action) => $action->dispatch('createItem'))
->addAction(fn (Action $action) => $action->dispatch('createItem'))
to the repeater and the listen on this event instead, but that doesn't seem to work. I'm guessing I can override the action, doing exactly the same as what is done in the Repeater class, and tacking my code on at the end. I'd like to avoid this thought, as that just seems wrong and if there is a change the repeater action I'll have to update this as well. Can someone point me in the direction of how this could be achieved.
10 Replies
Patrick Boivin
Patrick Boivin11mo ago
What is the modification you're trying to do when an item is added?
Oliverdam
Oliverdam11mo ago
I want to set the values in the new item, based off what the values are in the previous item. I.e. if the order field in the previous item was 3 the new item should have the order field prefilled with 4.
Patrick Boivin
Patrick Boivin11mo ago
Can you share your repeater code?
Oliverdam
Oliverdam11mo ago
I had something like this in v2
Repeater::make('questions')
->schema([
TextInput::make('order'),
])
->registerListeners([
['repeater::createItem' => [
function (Component $component, string $statePath) {
if ($statePath !== $component->getStatePath()) {
return;
}
$component->state(function ($get) {
$questions = collect($get('questions'));
$newQuestionKey = $questions->keys()->last();
$previousQuestion = $questions->at(-2);

$questions[$newQuestionKey] = [
'order' => $previousQuestion['order'] + 1,
];
return $questions;
});
},
],]
]);
Repeater::make('questions')
->schema([
TextInput::make('order'),
])
->registerListeners([
['repeater::createItem' => [
function (Component $component, string $statePath) {
if ($statePath !== $component->getStatePath()) {
return;
}
$component->state(function ($get) {
$questions = collect($get('questions'));
$newQuestionKey = $questions->keys()->last();
$previousQuestion = $questions->at(-2);

$questions[$newQuestionKey] = [
'order' => $previousQuestion['order'] + 1,
];
return $questions;
});
},
],]
]);
And in v3 I tried adding
->addAction(fn ($action) => $action->dispatch('addItem'))
->addAction(fn ($action) => $action->dispatch('addItem'))
To the repeater and changing the event listener to listen for this instead. Which might be incorrect use of it, I couldn't completely wrap my mind around how it worked.
ModestasV
ModestasV11mo ago
Wouldn't this work?
->addAction(fn(Action $action) => $action->after(function () {
// Do whatever is needed?
}))
->addAction(fn(Action $action) => $action->after(function () {
// Do whatever is needed?
}))
It might look like this:
Forms\Components\Repeater::make('invoiceProducts')
->relationship()
->live()
->columns([])
->addAction(fn(Action $action) => $action->after(function () {
// Do whatever is needed?
}))
Forms\Components\Repeater::make('invoiceProducts')
->relationship()
->live()
->columns([])
->addAction(fn(Action $action) => $action->after(function () {
// Do whatever is needed?
}))
I have this exact same logic applied for Delete action and it works just fine
Patrick Boivin
Patrick Boivin11mo ago
Maybe something like this?
->after(function ($livewire) {
$livewire->dispatch('addItem');
}))
->after(function ($livewire) {
$livewire->dispatch('addItem');
}))
Oliverdam
Oliverdam11mo ago
Doesn't seem to work either.
Patrick Boivin
Patrick Boivin11mo ago
Where are you at in your experimentation? What is the code you're trying on v3?
Oliverdam
Oliverdam11mo ago
@.modestasv I'm really sorry, I missed your answer before. I only saw it when I went through the whole thread once again 🤦‍♂️ Your answer actually works. This is what I made work.
->addAction(fn ($action) => $action->after(function (Repeater $component) {
$component->state(function ($state) {
$answers = collect($state);
$newAnswerKey = $answers->keys()->last();
$previousAnswer = $answers->at(-2);

$answers[$newAnswerKey] = [
'order' => $previousAnswer['order'] + 1,
];

return $answers->toArray();
});
}))
->addAction(fn ($action) => $action->after(function (Repeater $component) {
$component->state(function ($state) {
$answers = collect($state);
$newAnswerKey = $answers->keys()->last();
$previousAnswer = $answers->at(-2);

$answers[$newAnswerKey] = [
'order' => $previousAnswer['order'] + 1,
];

return $answers->toArray();
});
}))
I added after() that @pboivin suggested to the Repeater, which is stupid of me cus that's a validation rule. Thank you both very much the help.
ModestasV
ModestasV11mo ago
No worries haha 😄