Skip to content

[LiveComponent] Dependent form fields logic broken when submitting the form #2240

Closed
@gremo

Description

@gremo

Minimal repro: https://github.com/gremo/github-issue-2240

I'm implementing a search form for employee work hours. The form should allow the following options: no filtering at all, filtering by office only, filtering by employee only and iltering by both office and employee.

Therefore, the model has no strict constraints, and the form fields are optional. The form works correctly if no filters are selected, or if either the office or the employee is selected, or if the office is selected first followed by the associated employee.

class WorkHoursSearch
{
    public ?Office $office = null;

    public ?Employee $employee = null;
}
class WorkHoursSearchForm extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder = new DynamicFormBuilder($builder);

        $builder
            ->add('office', EnumType::class, [
                'class' => Office::class,
                'choice_label' => fn (Office $office): string => $office->getReadable(),
                'placeholder' => 'Any office',
                'required' => false,
            ])
            ->addDependent('employee', 'office', function (DependentField $field, ?Office $office) {
                $field->add(EnumType::class, [
                    'class' => Employee::class,
                    'placeholder' => 'Any emplyee',
                    'choices' => $office?->getEmployeeChoices(),
                    'choice_label' => fn (Employee $employee): string => $employee->getReadable(),
                    'required' => false,
                ]);
            })
        ;
    }

    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setDefaults(['data_class' => WorkHoursSearch::class]);
    }
}

When do problems occur? They happen when, after selecting the office and employee, the user decides to change the office.

Office 1 -> Employee 1 -> Office 2 -> Submit

scanario1

In this case (scenario 1) the selected employee is no longer part of the user list for the new office, but the previous value is still sent with the form (because form values prop is a LiveProp), so the form is invalid (an unknown value is present).

A more critical issue (scenario 2) occurs when the office has no associated employees:

Office 1 -> Employee 1 -> Office 3 -> Submit

scanario2

In this case, there’s no way to make the form valid and processable, even by selecting "Any employee." While this would understandably result in zero results, that's not the main concern.

In both cases there's no feedback provided to the user, no errors, no one knows what's happening.

I genuinely believe this is an issue that needs to be addressed. Personally, in my case, I don't see any solution, not even a workaround, but I still need to try something with JavaScript. Ideally, if field B depends on A to function, then when A changes, B should always be reset in some way. Otherwise, as described, the old value of B, which makes the form invalid, will continue to be sent to the server.

View old question here

I think there is a very obvious issue with the dependant form fields UX feature, take the meal example.

Let's add the logic for handing the form submission:

#[AsLiveComponent]
class MealPlanner extends AbstractController
{
    // ...

    #[LiveAction]
    public function save()
    {
        $this->submitForm();

        dd($this->getForm()->getData());
    }
}

Change also the form action and add a submit button:

{# ... #}

{{ form_start(form, {
    attr: {
        novalidate: true,
        'data-action': 'live#action:prevent',
        'data-live-action-param': 'save',
    }
}) }}
    <button>Save</button>
{{ form_end(form) }}

{# ... #}

Now let's use the form:

  1. Select breakfast as meal
  2. Select egg as food
  3. Change meal to lunch (eggs are NOT in lunch choices)

Until now the form re-renders correcly with HTTP 200 status code. No validation errors are shown.

Now submit the form pressing the "save" button:

  • 422 Unprocessable Content is returned, because the form is invalid
  • The "eggs" value is sent even if I didn't selected the food option (the placeholder "What's for lunch" is selected)
  • Value "eggs" is not in the lunch options, so it's an extraneous/invalid value
  • I think this is happening because of the nature of live components
  • No validation errors are shown!

image

image

Here is a short video of the steps:

change

I opened a similar issue SymfonyCasts/dynamic-forms#35 but I think that problem is not related to the library itself.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions