F
Filament10mo ago
benzo

Wizard modal with custom modal content

I am passing data to a custom modal content which is a wizard. Custom data is showing when the modal opens, but when clicking on the button to go to the next step, I am loosing the custom data passed to the custom view. The goal is to perform a custom modal action based on the custom data. This action may be performed at every steps, not sure what am I missing. Ideas?
//sandbox.blade.php
<div>
@foreach ($participants as $participant)
<div>
{{ $participant->name }} - {{ $participant->email }}
</div>
{{ ($this->edit)(['participant' => $participant->id]) }}
@endforeach
<x-filament-actions::modals />
</div>
//sandbox.blade.php
<div>
@foreach ($participants as $participant)
<div>
{{ $participant->name }} - {{ $participant->email }}
</div>
{{ ($this->edit)(['participant' => $participant->id]) }}
@endforeach
<x-filament-actions::modals />
</div>
//advance.blade.php
<div>
$arguments : @json($arguments)
{{ $action->getModalAction('report') }}
</div>
//advance.blade.php
<div>
$arguments : @json($arguments)
{{ $action->getModalAction('report') }}
</div>
//Sandbox.php
class Sandbox extends Component implements HasActions, HasForms
{
use InteractsWithActions, InteractsWithForms;

public function editAction()
{
return Action::make('edit')

->fillForm(function (array $arguments) {
return Participant::find($arguments['participant'])->toArray();
})
->registerModalActions([
Action::make('report')
->action(fn (array $arguments) => Log::info('report', $arguments)),
])
->action(fn (array $arguments) => Log::info('action', $arguments)
)
->modalContent(fn (array $arguments, Action $action): View => view(
'filament.pages.actions.advance',
['arguments' => $arguments, 'action' => $action]
))
->steps([
Step::make('Name')
->schema([
TextInput::make('name'),
]),
Step::make('Email')
->schema([
TextInput::make('email'),
]),
]);
}
}
//Sandbox.php
class Sandbox extends Component implements HasActions, HasForms
{
use InteractsWithActions, InteractsWithForms;

public function editAction()
{
return Action::make('edit')

->fillForm(function (array $arguments) {
return Participant::find($arguments['participant'])->toArray();
})
->registerModalActions([
Action::make('report')
->action(fn (array $arguments) => Log::info('report', $arguments)),
])
->action(fn (array $arguments) => Log::info('action', $arguments)
)
->modalContent(fn (array $arguments, Action $action): View => view(
'filament.pages.actions.advance',
['arguments' => $arguments, 'action' => $action]
))
->steps([
Step::make('Name')
->schema([
TextInput::make('name'),
]),
Step::make('Email')
->schema([
TextInput::make('email'),
]),
]);
}
}
Solution:
Hey @Patrick Boivin thanks for the hint, sometimes I just forgot I am inside a LW component! This is what I ended up with: ```php //Sandbox.php class Sandbox extends Component implements HasActions, HasForms...
Jump to solution
17 Replies
benzo
benzo10mo ago
$arguments : @json($arguments) //shows the participant ID when modal opens then becomes empty when going to wizard's next step
$arguments : @json($arguments) //shows the participant ID when modal opens then becomes empty when going to wizard's next step
benzo
benzo10mo ago
Patrick Boivin
Patrick Boivin9mo ago
I'm not sure you can persist action arguments like this... Can you instead write them to a public property on the Livewire component?
Solution
benzo
benzo9mo ago
Hey @Patrick Boivin thanks for the hint, sometimes I just forgot I am inside a LW component! This is what I ended up with:
//Sandbox.php
class Sandbox extends Component implements HasActions, HasForms
{
use InteractsWithActions, InteractsWithForms;

public Participant $participant;

public function editAction()
{
return Action::make('edit')

->mountUsing(function (array $arguments, Form $form) {
$this->participant = Participant::find($arguments['id']);
$form->fill($this->participant->toArray());
})

->registerModalActions([
Action::make('report')
->action(fn () => Log::info('report', $this->participant)),
])
->action(fn () => Log::info('action', $this->participant)
)
->modalContent(fn (array $arguments, Action $action): View => view(
'filament.pages.actions.advance',
['arguments' => $arguments, 'action' => $action]
))
->steps([
Step::make('Name')
->schema([
TextInput::make('name'),
]),
Step::make('Email')
->schema([
TextInput::make('email'),
]),
]);
}
}
//Sandbox.php
class Sandbox extends Component implements HasActions, HasForms
{
use InteractsWithActions, InteractsWithForms;

public Participant $participant;

public function editAction()
{
return Action::make('edit')

->mountUsing(function (array $arguments, Form $form) {
$this->participant = Participant::find($arguments['id']);
$form->fill($this->participant->toArray());
})

->registerModalActions([
Action::make('report')
->action(fn () => Log::info('report', $this->participant)),
])
->action(fn () => Log::info('action', $this->participant)
)
->modalContent(fn (array $arguments, Action $action): View => view(
'filament.pages.actions.advance',
['arguments' => $arguments, 'action' => $action]
))
->steps([
Step::make('Name')
->schema([
TextInput::make('name'),
]),
Step::make('Email')
->schema([
TextInput::make('email'),
]),
]);
}
}
Patrick Boivin
Patrick Boivin9mo ago
Looking good!
benzo
benzo9mo ago
OK, making progress here, but facing a new wall
->registerModalActions([
Action::make('report')
->action(fn (Form $form) => $this->participant->saveAsDraft($form)), // ($form) must be of type Filament\Forms\Form, null given.
])
->registerModalActions([
Action::make('report')
->action(fn (Form $form) => $this->participant->saveAsDraft($form)), // ($form) must be of type Filament\Forms\Form, null given.
])
The $form is not available at this level, I partially solved it by using the LW component which is accessible, [not ideal as I cannot fire validation] but I just want to know if it is the good way to do it
->registerModalActions([
Action::make('report')
->action(fn (Component $livewire) => $this->participant->saveAsDraft($livewire->mountedActionsData[0])), // handling the array data.
])
->registerModalActions([
Action::make('report')
->action(fn (Component $livewire) => $this->participant->saveAsDraft($livewire->mountedActionsData[0])), // handling the array data.
])
Maybe there is another way to make another button where the form data could be accessible. Ideas?
Patrick Boivin
Patrick Boivin9mo ago
not ideal as I cannot fire validation
Can you expand on this? Are you not getting the regular validation from the form fields in the modal?
benzo
benzo9mo ago
Sure! when clicking on the next button on the modal wizard, form validation happens. But when clicking on the modalAction button, there is no validation. My modalAction needs the form data (adn validation ) to be performed. That's why I tried to pass the Form object into the callback but the object is null. For now I retrieve the form data from the LW component but without validation. Not sure if I can access the form object from the LW object
Patrick Boivin
Patrick Boivin9mo ago
Just a thought - can you disable the modal's Submit button and instead use a Submit button on the final step in the Wizard? https://filamentphp.com/docs/3.x/forms/layout/wizard#rendering-a-submit-button-on-the-last-step
benzo
benzo9mo ago
Ok, If I add ->modalSubmitAction(false), it removes the submit button on the final step. I can not add a submitAction(),this method is not available as this is not a Wizard object but an Action Modal with Steps. I could add another modal action 'submit the form' but I am not sure if I can retreive the "latest" step of the wizard to only display it on this condition. I am not sure to follow what you me to do!
Patrick Boivin
Patrick Boivin9mo ago
Wow, this is more complex than it seems after all. So another thought - Instead of using the steps() on the action modal, can you use form() and pass in your Wizard object? I believe you'll be able to use a submit action on the last step as documented.
benzo
benzo9mo ago
Complex indeed! Good idea, I will try with a form, I'll keep you updated 👍 Hey @Patrick Boivin , I ended up by making a form with a wizard inside as you cleverly advised. With this solution where I can have a better control on form data. I guess using steps is quick for basic wizard forms. Thanks for your guidances, learned a lot here!
Patrick Boivin
Patrick Boivin9mo ago
You're welcome @benzo, I learned something too!
shojibflamon
shojibflamon9mo ago
What is the solution @benzo . Could you please say some more details so that I can achieve this?
benzo
benzo9mo ago
Hey @shojibflamon, I have a boolean $draft property at the LW component level. Then my custom modal content has a "Save as draft" button firing the submit form action. This button contains a specific directive ( wire:click="$set('draft', true)" ) that allows me to have form validation and knowing in the same time if the submit action is a draft or not. This is my use case but it depends what you would like to achieve.
shojibflamon
shojibflamon8mo ago
Yes almost like this. Wanna save as draft in the middle of the larage form. Could you please share your solution so that I can incorporate in my project.
benzo
benzo8mo ago
->submitAction(
new HtmlString(
Blade::render(<<<'BLADE'
<x-filament::button
type="submit"
wire:click="$set('draft', false)"
>
Submit
</x-filament::button>
BLADE
)
)
)
->submitAction(
new HtmlString(
Blade::render(<<<'BLADE'
<x-filament::button
type="submit"
wire:click="$set('draft', false)"
>
Submit
</x-filament::button>
BLADE
)
)
)
put your Draft button wherever you want with :
wire:click="$set('draft', true)"
wire:click="$set('draft', true)"
Then check the draft value into your submit logic