F
Filament2y ago
John

Custom Wizard layout component

(This is a follow up on https://discord.com/channels/883083792112300104/1085179668816212009) I'm trying to create and use my custom layout component \App\Forms\Components\Wizard by extending \Filament\Forms\Components\Wizard. - The layout is different (steps are displayed vertically) - I want to save the form at every [Next >] / [< Previous] I'm struggling with where/how to use my Widget class instead of the package one. The only place I could find where the Widget class is actually used is in \Filament\Resources\Form::getSchema. Does that mean that I need my custom Form class as well? After that I get lost in a web of inheritance in traits of which I don't know which I need overriding and how. I then tried to simply return my \App\Forms\Components\Wizard in \App\Filament\Resources\RequestResource\Pages\EditRequest::getFormSchema. It gets rendered as flat form fields, without a Widget "around" it. Then I took the wizard.blade.php from Filament. I do get my Widget rendered now! But, the [Next >] button (and perhaps more?) doesn't work. A LiveWire POST is fired with 200 OK response, no console errors, but my Wizard stays at the first step. The Filament Wizard hits the wizard::nextStep listener in vendor/filament/forms/src/Components/Wizard.php:45 but my custom Wizard does not. 1. Do I need a custom Form class, HasWizard traits etc. to get this working properly? 2. Why is the [Next >] button not working and how can I fix it?
4 Replies
josef
josef2y ago
Can you provide a repository with your code? Without seeing the details of your implementation it's hard to see where the problem might lie
John
JohnOP2y ago
I've investigated some further. I started out with implementing the default Wizard from the docs (https://filamentphp.com/docs/2.x/admin/resources/creating-records#wizards). Next step towards my custom Wizard, I implemented some stuff from the HasWizard trait:
<?php

namespace App\Filament\Resources\RequestResource\Pages;

use App\Filament\Resources\RequestResource;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Wizard;
use Filament\Forms\Components\Wizard\Step;
use Filament\Resources\Form;
use Filament\Resources\Pages\EditRecord;

class EditRequest extends EditRecord
{
protected static string $resource = RequestResource::class;

protected function form(Form $form): Form
{
return $form
->wizard()
->modifyBaseComponentUsing(function (Wizard $component) {
$component
->startOnStep(1)
->cancelAction($this->getCancelFormAction())
->submitAction($this->getSubmitFormAction())
->skippable(false);
})
->schema([
Step::make('First')->schema([TextInput::make('first_name')]),
Step::make('Second')->schema([TextInput::make('last_name')])
]);
}

protected function getFormActions(): array
{
return [];
}
}
<?php

namespace App\Filament\Resources\RequestResource\Pages;

use App\Filament\Resources\RequestResource;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Wizard;
use Filament\Forms\Components\Wizard\Step;
use Filament\Resources\Form;
use Filament\Resources\Pages\EditRecord;

class EditRequest extends EditRecord
{
protected static string $resource = RequestResource::class;

protected function form(Form $form): Form
{
return $form
->wizard()
->modifyBaseComponentUsing(function (Wizard $component) {
$component
->startOnStep(1)
->cancelAction($this->getCancelFormAction())
->submitAction($this->getSubmitFormAction())
->skippable(false);
})
->schema([
Step::make('First')->schema([TextInput::make('first_name')]),
Step::make('Second')->schema([TextInput::make('last_name')])
]);
}

protected function getFormActions(): array
{
return [];
}
}
That ->wizard() ultimately replaces the base component with a Wizard in \Filament\Resources\Form::getSchema. I have no idea how I can mimic that behaviour with my custom Wizard. I tried replacing the Form class with my own, replacing getSchema, but I'm not allowed (declaration incompatible).
josef
josef2y ago
just make the declaration compatible? Are you trying to replace the original wizard, or mimic the behavior using a custom component? the latter shouldn't be that hard, in principle. Maybe would even be enough to just use a different view on the filament wizard component
John
JohnOP2y ago
I'm making a custom component, and wanted to extend the base Wizard class to use as much as possible from Filament. I'm a bit further again. I managed to fix the declaration by hinting two classes, so it's compatible with the package base class as well as with what I actually return. My Edit page:
<?php

namespace App\Filament\Resources\RequestResource\Pages;

use App\Filament\Form;
use App\Filament\Resources\RequestResource;
use App\Forms\Components\Wizard;
use Filament\Forms\Components\Wizard\Step;
use Filament\Resources\Pages\EditRecord;


class EditRequest extends EditRecord
{
protected static string $resource = RequestResource::class;

protected function form($form): Form|\Filament\Resources\Form
{
return $form
->wizard()
->modifyBaseComponentUsing(function (Wizard $component) {
$component
->startOnStep(1)
->cancelAction($this->getCancelFormAction())
->submitAction($this->getSubmitFormAction())
->skippable(false);
})
->schema([
Step::make('First')->schema([TextInput::make('first_name')]),
Step::make('Second')->schema([TextInput::make('last_name')])
]);
}

protected function getFormActions(): array
{
return [];
}

protected function getBaseResourceForm(?int $columns = null, bool $isDisabled = false): \Filament\Resources\Form
{
return App\Filament\Form::make()
->columns($columns)
->disabled($isDisabled);
}
}
<?php

namespace App\Filament\Resources\RequestResource\Pages;

use App\Filament\Form;
use App\Filament\Resources\RequestResource;
use App\Forms\Components\Wizard;
use Filament\Forms\Components\Wizard\Step;
use Filament\Resources\Pages\EditRecord;


class EditRequest extends EditRecord
{
protected static string $resource = RequestResource::class;

protected function form($form): Form|\Filament\Resources\Form
{
return $form
->wizard()
->modifyBaseComponentUsing(function (Wizard $component) {
$component
->startOnStep(1)
->cancelAction($this->getCancelFormAction())
->submitAction($this->getSubmitFormAction())
->skippable(false);
})
->schema([
Step::make('First')->schema([TextInput::make('first_name')]),
Step::make('Second')->schema([TextInput::make('last_name')])
]);
}

protected function getFormActions(): array
{
return [];
}

protected function getBaseResourceForm(?int $columns = null, bool $isDisabled = false): \Filament\Resources\Form
{
return App\Filament\Form::make()
->columns($columns)
->disabled($isDisabled);
}
}
The sole purpose of my custom Form is that I can return my custom Wizard in getSchema(). My wizard is displayed now. But I'm back to this situation:
But, the [Next >] button (and perhaps more?) doesn't work. A LiveWire POST is fired with 200 OK response, no console errors, but my Wizard stays at the first step. The Filament Wizard hits the wizard::nextStep listener in vendor/filament/forms/src/Components/Wizard.php:45 but my custom Wizard which extends the package one, does not.
It's working now! At some point in time I thought I had to override Wizard::make and I skipped the setup part. Next step is actually customising it to my needs (saving at every Next and Previous button press). But at least I can use it now and it's working. Thanks @josef !
Want results from more Discord servers?
Add your server