How to validate a modal form before extraModalFooterActions runs?

I have a modal with a form and a custom footer button. I need to validate the form when this custom button is clicked, before its own logic runs. However, the action's ->before() hook is never triggered, so I can't run validation. Here is a simplified example of my code:
Action::make('formModal')
->form([
\Filament\Forms\Components\TextInput::make('name')->rules('required')->markAsRequired(),
])
->modalSubmitAction(false) // Disable default submit button
->extraModalFooterActions([
Action::make('confirmAction')
// PROBLEM: This hook is never called.
->before(function () {
// I want to validate the 'name' field here,
// but this code never runs.
dd('This is never reached');
})
->action(function () {
// This runs without validation.
}),
]),
Action::make('formModal')
->form([
\Filament\Forms\Components\TextInput::make('name')->rules('required')->markAsRequired(),
])
->modalSubmitAction(false) // Disable default submit button
->extraModalFooterActions([
Action::make('confirmAction')
// PROBLEM: This hook is never called.
->before(function () {
// I want to validate the 'name' field here,
// but this code never runs.
dd('This is never reached');
})
->action(function () {
// This runs without validation.
}),
]),
Is there a way to validate the form before proceeding to the modal footer action? (I don't want to validate by ->required())
9 Replies
Rolland
Rolland3mo ago
this is how i do it:
$this->extraModalFooterActions(fn (Action $action) => [
$this->getSubmitAction(),
$this->getSaveDraftAction(),
]);
$this->extraModalFooterActions(fn (Action $action) => [
$this->getSubmitAction(),
$this->getSaveDraftAction(),
]);
private function getSaveDraftAction(): Action
{
return Action::make('save_draft')
->label('Save as Draft')
->color('gray')
->action(function (Component $livewire) {
$this->processAction($livewire, ['isDraft' => true]);
})
->after(fn (Action $action) => $action->cancelParentActions());
}
private function getSaveDraftAction(): Action
{
return Action::make('save_draft')
->label('Save as Draft')
->color('gray')
->action(function (Component $livewire) {
$this->processAction($livewire, ['isDraft' => true]);
})
->after(fn (Action $action) => $action->cancelParentActions());
}
private function processAction(Component $livewire, array $arguments): void
{
// trigger validation manually.
$livewire->validate(rules:[
'key' => 'value',
], attributes: [
'key' => 'value',
]);
}
private function processAction(Component $livewire, array $arguments): void
{
// trigger validation manually.
$livewire->validate(rules:[
'key' => 'value',
], attributes: [
'key' => 'value',
]);
}
you will need to manually trigger the validation.
toeknee
toeknee3mo ago
No need to do the above, just run: $formData = $this->form->getState(); This runs the validation and only returns the formData for saving. so:
Action::make('confirmAction')
// PROBLEM: This hook is never called.
->action(function () {
$formData = $this->form->getState();
}),
Action::make('confirmAction')
// PROBLEM: This hook is never called.
->action(function () {
$formData = $this->form->getState();
}),
ybzhnitees
ybzhniteesOP3mo ago
When clicking the 'Confirm' button, it proceeds to the confirmation modal. What I want is to prevent the confirmation modal from opening if the form hasn't passed validation. If I use $this->getSaveDraftAction() or $formData = $this->form->getState() in extraModalFooterActions, the confirmation modal still opens immediately. Is there another way to achieve this?
No description
toeknee
toeknee3mo ago
Not before confirmation that I am aware
Rolland
Rolland3mo ago
I have tried this solution and that did not work for me. Which is why I provide my solution.
toeknee
toeknee3mo ago
getState runs the entire form validation, if it isn't then you likely haven't mounted your data correctly. Or you want individual validations to be run which yours will do.
Rolland
Rolland3mo ago
probably the reason it does not work for me is because i create a custom action class.
class CreateItrfStageOneAction extends BasePageAction
{
use CanCustomizeProcess, ItrfFormHandler;

...

protected function setUp(): void
{
parent::setUp();

$this->label('Create ITRF');
$this->icon('heroicon-o-plus');
$this->color('success');
$this->slideOver();
// ItrfFormHandler
$this->form($this->generateItrfStageOneForm(...));

$this->extraModalFooterActions(fn (Action $action) => [
$this->getSubmitAction(),
$this->getSaveDraftAction(),
]);
class CreateItrfStageOneAction extends BasePageAction
{
use CanCustomizeProcess, ItrfFormHandler;

...

protected function setUp(): void
{
parent::setUp();

$this->label('Create ITRF');
$this->icon('heroicon-o-plus');
$this->color('success');
$this->slideOver();
// ItrfFormHandler
$this->form($this->generateItrfStageOneForm(...));

$this->extraModalFooterActions(fn (Action $action) => [
$this->getSubmitAction(),
$this->getSaveDraftAction(),
]);
toeknee
toeknee3mo ago
More than likely then, it'll be down to the form filling I suspect on your custom action. Though I haven't gone that deep as not needed too as of yet.

Did you find this page helpful?