F
Filament2mo ago
alan

Save Wizard on clicking next button with alpineClickHandler

I have a question with limited resources online. I want to auto-save responses after a step in a Wizard. From Dan's comment under this video, I learned that AlpineJS can trigger an existing action or javascript. 1. How can I log something to the console or show an alert on clicking "Next"?
2. How can I auto-save responses while using AlpineJS to trigger actions?
Here's an example snippet:
Wizard::make([
Wizard\Step::make(__('Step1'))
->schema([
// ...
])->afterValidation(function () {
Notification::make()
->title('Saved successfully')
->success()
->send();
Action::make('next')
->alpineClickHandler('console.log("next")');
}
)
Wizard::make([
Wizard\Step::make(__('Step1'))
->schema([
// ...
])->afterValidation(function () {
Notification::make()
->title('Saved successfully')
->success()
->send();
Action::make('next')
->alpineClickHandler('console.log("next")');
}
)

This doesn't work. afterValidation works, but Action::make('next')->alpineClickHandler doesn't Thank you!
Filament Daily
YouTube
Filament Wizard: Summary Step with Edit Button
If you want to users to double-check the information before submitting, here's how to do it in a Wizard. Full source code: https://filamentexamples.com/project/wizard-with-summary-overview-and-back-buttons More Filament examples on our website: https://filamentexamples.com
10 Replies
alan
alanOP2mo ago
I found a solution to log to the console when clicking "Next." Now, I need to figure out how to trigger client-side saving using AlpineJS. Any tips are appreciated.
Wizard::make([
Wizard\Step::make(__('Step 1'))
->schema([
...
]),
Wizard\Step::make(__('Step 2'))
->schema([
...
]),
])
->nextAction(fn (Action $action) => $action->alpineClickHandler('alert("Next step")'));
Wizard::make([
Wizard\Step::make(__('Step 1'))
->schema([
...
]),
Wizard\Step::make(__('Step 2'))
->schema([
...
]),
])
->nextAction(fn (Action $action) => $action->alpineClickHandler('alert("Next step")'));
->nextAction(fn (Action $action) => $action->alpineClickHandler('$wire.save()'))
->nextAction(fn (Action $action) => $action->alpineClickHandler('$wire.save()'))
I found a way to save the form when clicking "Next." But the problem is that I only want to save the form when it’s already created and I'm editing it. Otherwise, I get errors when the entry isn't created yet, as I can't save something that isn’t created. So, how can I limit it so the $wire.save() function gets called only when editing? Additionally I would like to disable the creation notification. has somebody an idea? The problem is still that I want to execute
$wire.save()
$wire.save()
on clicking the next button in a wizard but only if I'm currently editing the record
toeknee
toeknee2mo ago
Why not just to afterStateValidated() on the step, then save it there.
alan
alanOP2mo ago
@toeknee thanks for your help I want to save it only, when I'm editing a record. That's why it won't work. Also I have 10 steps so calling this function after each step seems a bit too much
toeknee
toeknee2mo ago
But you’re wanting it to save on each step….
alan
alanOP2mo ago
Ok I can do it like you said and explained in this youtube method https://www.youtube.com/watch?v=lOPNHvIggqI&t=117s. But what should I do so it only works when editing (otherwise when I go back and forth a lot, this will create serval records in the db). I don't want to specify getSteps two times, in create and edit with this small difference. It would be a code smell. Do you have a tip?
Filament Daily
YouTube
Filament Wizard: Save DB "Draft" After "Next" on First Step
If you have a long form and want to save some fields portion by portion, here's how to do it in Wizard. Related video: Filament Long Form: Wizard in Create Page https://www.youtube.com/watch?v=hQ2LDKm7XMk More Filament examples on our website: https://filamentexamples.com
No description
Matthew
Matthew2mo ago
Just write a custom action handler to provide the logic of when to save\create or not.
alan
alanOP2mo ago
Thanks @Matthew but unfortunately I don't know how to approach this. In my earlier messages I think I wrote something similar to an action handler but my question is: how can I distinct between edit and creating? this is my current state of code Models/Form.php
<?php

namespace App\Models;

...

class Form extends Model
{
use HasFactory;

protected $casts = [
...
];

public static function getForm(): array
{
return [
Wizard::make([
Wizard\Step::make('Step 1'),
...
Wizard\Step::make('Step 2'),
Wizard\Step::make('Step 3'),
Wizard\Step::make('Step 4')
])
->nextAction(fn (Action $action) => $action->alpineClickHandler('$wire.save()'))
];
}
}
<?php

namespace App\Models;

...

class Form extends Model
{
use HasFactory;

protected $casts = [
...
];

public static function getForm(): array
{
return [
Wizard::make([
Wizard\Step::make('Step 1'),
...
Wizard\Step::make('Step 2'),
Wizard\Step::make('Step 3'),
Wizard\Step::make('Step 4')
])
->nextAction(fn (Action $action) => $action->alpineClickHandler('$wire.save()'))
];
}
}
Filament/Resources/FormResource.php
<?php

namespace App\Filament\Resources;

...

class FormResource extends Resource
{
protected static ?string $model = Form::class;

public static function form(Form $form): Form
{
return $form
->schema(Form::getForm());
}

...
<?php

namespace App\Filament\Resources;

...

class FormResource extends Resource
{
protected static ?string $model = Form::class;

public static function form(Form $form): Form
{
return $form
->schema(Form::getForm());
}

...
Filament/Resources/FormResource/Pages/CreateForm.php
<?php

namespace App\Filament\Resources\FormResource\Pages;

...

class CreateForm extends CreateRecord
{
protected static string $resource = FormResource::class;
}
<?php

namespace App\Filament\Resources\FormResource\Pages;

...

class CreateForm extends CreateRecord
{
protected static string $resource = FormResource::class;
}
Filament/Resources/FormResource/Pages/EditForm.php
<?php

namespace App\Filament\Resources\FormResource\Pages;

...

class EditForm extends EditRecord
{
protected static string $resource = FormResource::class;

...
}
<?php

namespace App\Filament\Resources\FormResource\Pages;

...

class EditForm extends EditRecord
{
protected static string $resource = FormResource::class;

...
}
Matthew
Matthew2mo ago
Not actually tried it...but I think:
->afterValidation(function ($record, $get) {

if ($record) {

$record->field1 = $get('field1');
$record->field2 = $get('field2');
.....
$record->save();

})
->afterValidation(function ($record, $get) {

if ($record) {

$record->field1 = $get('field1');
$record->field2 = $get('field2');
.....
$record->save();

})
If it is a CreateAction, the $record will be null, so it won't fire
alan
alanOP2mo ago
Ok nice thank you it worked
->afterValidation(function ($record, $get) {
$record->members_amount = $get('members_amount');
$record->members_other_benefits = $get('members_other_benefits');
...
$record->save();
}),
->afterValidation(function ($record, $get) {
$record->members_amount = $get('members_amount');
$record->members_other_benefits = $get('members_other_benefits');
...
$record->save();
}),
However, since I have multiple steps in the wizard, this approach results in repetitive code and creates unnecessary overhead. Is there a more maintainable method that can be used across all steps in Filament? And just to be sure: afterValidation only executes after successful validations, right?
Matthew
Matthew2mo ago
Sure, just elevate that to a custom handler and call the handler each time.

Did you find this page helpful?