F
Filament5mo ago
Noxo

Dynamic form - CheckboxList issue

I have a part of a form that is generated automatically based on a template. A value is selected in the first dropdown list. and the form below is built based on it. I encountered a problem when after building the form, the CheckboxList field does not work correctly. If you have any ideas, please let me know 🙏
Solution:
also was reading more on how on this https://filamentphp.com/docs/3.x/forms/advanced#dynamic-fields-based-on-a-select-option it did worked but it always fill the data with null, so maybe you can do something there!...
Jump to solution
14 Replies
Noxo
NoxoOP5mo ago
// resource -> form

Forms\Components\Tabs::make('Tabs')->tabs([
Forms\Components\Tabs\Tab::make('Processors')->schema([
Forms\Components\Select::make('processors')
->hiddenLabel()
->relationship('processors', 'name', modifyQueryUsing: fn ($query) => $query->active())
->searchable()
->multiple()
->live()
->preload(),
])->columns(2),
]),

Forms\Components\Tabs::make('ProcessorTabs')->tabs(function (callable $get) {
$processorIds = (array) $get('processors');
$processors = Processor::whereIn('id', $processorIds)->active()->get();
$tabs = [];

foreach ($processors as $processor) {
$processorId = $processor->id;

$tabs[] = Forms\Components\Tabs\Tab::make("processor.{$processorId}")
->label($processor->name)
->icon('heroicon-m-building-office-2')
->schema([
Forms\Components\Tabs::make('InnerProcessorTabs')->tabs(
$processor->templates()->active()->get()->map(
fn ($template) => Forms\Components\Tabs\Tab::make("processor.{$processorId}.{$template->id}")
->label($template->name)
->icon('heroicon-m-document-text')
->columns(2)
->statePath("questionnaire_data.{$processorId}.{$template->id}")
->schema(TemplateQuestionnaireForm::getScheme($template)),
)->toArray(),
),
]);
}

return $tabs;
})
// resource -> form

Forms\Components\Tabs::make('Tabs')->tabs([
Forms\Components\Tabs\Tab::make('Processors')->schema([
Forms\Components\Select::make('processors')
->hiddenLabel()
->relationship('processors', 'name', modifyQueryUsing: fn ($query) => $query->active())
->searchable()
->multiple()
->live()
->preload(),
])->columns(2),
]),

Forms\Components\Tabs::make('ProcessorTabs')->tabs(function (callable $get) {
$processorIds = (array) $get('processors');
$processors = Processor::whereIn('id', $processorIds)->active()->get();
$tabs = [];

foreach ($processors as $processor) {
$processorId = $processor->id;

$tabs[] = Forms\Components\Tabs\Tab::make("processor.{$processorId}")
->label($processor->name)
->icon('heroicon-m-building-office-2')
->schema([
Forms\Components\Tabs::make('InnerProcessorTabs')->tabs(
$processor->templates()->active()->get()->map(
fn ($template) => Forms\Components\Tabs\Tab::make("processor.{$processorId}.{$template->id}")
->label($template->name)
->icon('heroicon-m-document-text')
->columns(2)
->statePath("questionnaire_data.{$processorId}.{$template->id}")
->schema(TemplateQuestionnaireForm::getScheme($template)),
)->toArray(),
),
]);
}

return $tabs;
})
// TemplateQuestionnaireForm.php

class TemplateQuestionnaireForm
{
public static function getScheme(Template $template)
{
return $template
->fields()
->active()
->get()
->map(fn ($field) => match ($field->type) {
TemplateFieldType::Checkbox => Forms\Components\CheckboxList::make($field->id)
->label($field->label)
->default($field->default)
->options($field->options)
->extraAttributes([
'class' => 'flex flex-wrap gap-2'
]),

TemplateFieldType::Text,
TemplateFieldType::Number => Forms\Components\TextInput::make($field->id)
->numeric(TemplateFieldType::Number->is($field->type))
->label($field->label)
->default($field->default)
->maxLength(255),
})
// ->dd()
->toArray();
}
}
// TemplateQuestionnaireForm.php

class TemplateQuestionnaireForm
{
public static function getScheme(Template $template)
{
return $template
->fields()
->active()
->get()
->map(fn ($field) => match ($field->type) {
TemplateFieldType::Checkbox => Forms\Components\CheckboxList::make($field->id)
->label($field->label)
->default($field->default)
->options($field->options)
->extraAttributes([
'class' => 'flex flex-wrap gap-2'
]),

TemplateFieldType::Text,
TemplateFieldType::Number => Forms\Components\TextInput::make($field->id)
->numeric(TemplateFieldType::Number->is($field->type))
->label($field->label)
->default($field->default)
->maxLength(255),
})
// ->dd()
->toArray();
}
}
Lara Zeus
Lara Zeus5mo ago
add live to the tabs?! I would add live to all component just to make sure for testing :), it seems the statePath not updating correctly in the first time
Noxo
NoxoOP5mo ago
added live to everything but the problem remained
No description
Lara Zeus
Lara Zeus5mo ago
you can dump or ray it and see the data:
->statePath(ray($processorId,$template->id))
->statePath(ray($processorId,$template->id))
Noxo
NoxoOP5mo ago
No description
No description
Lara Zeus
Lara Zeus5mo ago
->statePath(fn()=>ray($processorId,$template->id); '';) or convert it to a closure for testing
Noxo
NoxoOP5mo ago
it was called five times 🤔
No description
No description
Lara Zeus
Lara Zeus5mo ago
also they are not unique! what is the state path after of the checkboxes the form saved (when it was working correctly in the video)
Noxo
NoxoOP5mo ago
the state path is always the same
Noxo
NoxoOP5mo ago
here is the code to reproduce it. could you try it on your side? https://gist.github.com/aqjw/98515c86bb9c4c360a73533cb0f27c32#file-form-php
Gist
form.php
GitHub Gist: instantly share code, notes, and snippets.
Lara Zeus
Lara Zeus5mo ago
not really sure, the moment I added
$this->form->fill([
'processors'=>['p1'],
]);
$this->form->fill([
'processors'=>['p1'],
]);
all working fine, I did test out side the tabs, but the same sorter code for testing:
return $form
->schema([
Select::make('processors')
->options(['p1' => 'p1', 'p2' => 'p2', 'p3' => 'p3'])
->multiple()
->live(),
Grid::make('processors_nest')->live()->schema(function (Get $get) {
$processors = (array) $get('processors');
$tabs = [];
foreach ($processors as $processor) {
$tabs[] = CheckboxList::make('dd'.$processor)
->options(['o1' => 'o1', 'o2' => 'o2', 'o3' => 'o3', 'o4' => 'o4'])
->live();
}
return $tabs;
})
])
->statePath('data');
return $form
->schema([
Select::make('processors')
->options(['p1' => 'p1', 'p2' => 'p2', 'p3' => 'p3'])
->multiple()
->live(),
Grid::make('processors_nest')->live()->schema(function (Get $get) {
$processors = (array) $get('processors');
$tabs = [];
foreach ($processors as $processor) {
$tabs[] = CheckboxList::make('dd'.$processor)
->options(['o1' => 'o1', 'o2' => 'o2', 'o3' => 'o3', 'o4' => 'o4'])
->live();
}
return $tabs;
})
])
->statePath('data');
Solution
Lara Zeus
Lara Zeus5mo ago
also was reading more on how on this https://filamentphp.com/docs/3.x/forms/advanced#dynamic-fields-based-on-a-select-option it did worked but it always fill the data with null, so maybe you can do something there!
Noxo
NoxoOP5mo ago
the sorter doesn't work for me either
Noxo
NoxoOP5mo ago
problem solved. it was necessary to add afterStateUpdated as shown in the docs
No description
Want results from more Discord servers?
Add your server