symfony: [DX] [Form] Ability to reset form validation errors (or prevent them from rendering)

I’ve implemented the cookbook solution for dynamic generation of submitted forms across several projects. Sometimes these forms need to add/update/remove several fields, so my JavaScript replaces the entire form - not just a single, hard-coded element like in the example. My controllers typically look something like this:

public function testAction(Request $request)
{
    $entity = new Entity();
    $form = $this->createForm('foo', $entity);

    $form->handleRequest($request);

    if ($form->isValid() && !$request->isXmlHttpRequest()) {
        $em = $this->getDoctrine()->getManager();
        $em->persist($entity);
        $em->flush($entity);

        return $this->redirectToRoute('controller_index');
    }

    return ['form' => $form->createView()];
}

The problem

This works pretty well, but there’s one major annoyance - AJAX submissions of incomplete forms result in lots of red errors everywhere. This occurs because Symfony validates the form by default during $form->handleRequest($request).

The cookbook recommends supressing form validation by stopping propagation of the POST_SUBMIT event. However, I have other services listening for that event, so this approach isn’t feasible.

I’m currently restoring to re-building the form just prior to the return:

// ...

// Don't show any errors on partial AJAX submissions
if ($request->isXmlHttpRequest()) {
    $form = $this->createForm('foo', $entity);
}

return ['form' => $form->createView()];

This works, but it seems kinda kludgy and not like an ideal solution, which is why I’ve tagged this [DX]. It would be great if Symfony could provide a built-in method to prevent this common problem without weird workarounds.

Proposed solutions

Would it be possible to add a clearErrors() method to FormInterface? Ideally this would remove any previously-set validation errors (as if the form were never validated).

Alternatively, perhaps we could have some way of easily hiding/removing errors from the view? Maybe a boolean flag on the createView method, a FormView::clearErrors method, a setting on the FormRenderer, or something to that effect?

About this issue

  • Original URL
  • State: closed
  • Created 9 years ago
  • Reactions: 5
  • Comments: 29 (13 by maintainers)

Commits related to this issue

Most upvoted comments

@fabpot Could we please reopen this? Or go forward with #23838?

The implementation for this is already done in #14233 and all that is required is the political will to merge it into some version.

The proposed workaround doesn’t work with FormTypes for entities also containing unmapped fields.

I have a case when I want to know if a form is valid or not and still not show the errors. So supressing the validation or setting an option in the FormType is not an option.

Could this be reconsidered?

As an alternative to #14233/#27571, I have implemented #27580 which adds a new ClearableFormInterface instead of modifying an existing interface. Not only does this avoid the BC-break, it also avoids us adding the method to things like Button which don’t support errors anyway.

@ouassini that is not the same. I have had occasions where I want the validation to be done as normal, but then for other reasons (depending on circumstances) want to clear all errors and present the form to the user again. This is a real need, and it would not cost much to add this flexibilty to the Symfony framework.

I finally found a solution with this documentation: http://symfony.com/doc/current/form/dynamic_form_modification.html#suppressing-form-validation

Create a form type extension especially for this event:

/**
 * Permits to deactivate form validation with ease.
 *
 * Useful for ajax reload.
 *
 * @author Sullivan Senechal <soullivaneuh@gmail.com>
 */
final class NoValidateFormTypeExtension extends AbstractTypeExtension
{
    /**
     * {@inheritdoc}
     */
    public function getExtendedType()
    {
        FormType::class;
    }

    /**
     * {@inheritdoc}
     *
     * @see http://symfony.com/doc/current/form/dynamic_form_modification.html#suppressing-form-validation
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        if ($options['no_validate']) {
            $builder->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) {
                $event->stopPropagation();
            }, 900);
        }
    }

    /**
     * {@inheritdoc}
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver
            ->setDefaults([
                'no_validate' => false,
            ])
            ->setAllowedTypes('no_validate', 'bool')
        ;
    }
}

Then, on you controller:

$form = $this->createForm(YourFormType::class, $data, [
    'no_validate' => $request->isXmlHttpRequest(),
]);

And voilà! 😄

But I think it would be great to just have an option for that. Or maybe on form handling process?

Maybe there is a bundle for that, but I didn’t find it.

And BTW, I’m not sure this is the “proper” solution because this method deactivate all FormEvents::POST_SUBMIT callbacks, not only validation.

If you have custom process based on this event, I think they will not be called.

+1 Unfortunately, I use the same trick… A way to bypass the assertions in the request handling process would be great (or even a clearErrors method).

Yes, you can pass it in as an option to the form, but then you lose the validation. Sometimes you want the validation to happen, but then depending on other if statements clear the errors and present the form again clear of errors. This is a feature for us power users, that wants flexibility in how we handle forms.

+1

+1