Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
colinodell opened this issue Mar 25, 2015 · 29 comments

Comments

@colinodell
Copy link
Contributor

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?

@dosten
Copy link
Contributor

dosten commented Mar 26, 2015

I have the same problem with a form that have a single select field, and depending on the selected choice show differents fields, a clearErrors method will be great!

@stof
Copy link
Member

stof commented Apr 7, 2015

Changing in in the view should be possible using a form type extension overwriting the variables valid and errors to reset them (based on an option added by the form type extension). This would allow solving the issue without any change to the form component

@colinodell
Copy link
Contributor Author

Thanks for the advice @stof, I'll work on implementing that method instead.

@webmozart
Copy link
Contributor

Closing, since this seems to be solved.

@iBasit
Copy link

iBasit commented Jan 14, 2016

I'm not sure if its bad to do, but I have solved this by recreating form after handleRequest. It has solved the issue.

$form = $this->createForm(new FormType, $dataModel);
$form->handleRequest($request); 
$form = $this->createForm(new FormType, $dataModel); // this will silent the errors and keeps new data and is valid is false

@colinodell
Copy link
Contributor Author

Yeah, I ended up resorting to that same workaround due to time constraints.

I really wish there was a better way to clear errors without restoring to this workaround, writing form type extensions, or complicating my view logic to sometimes hide them.

@iBasit
Copy link

iBasit commented Jan 14, 2016

+1

@lenybernard
Copy link

+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).

@JarJak
Copy link

JarJak commented Oct 17, 2016

+1

@ampaze
Copy link
Contributor

ampaze commented Oct 19, 2016

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?

@colinodell
Copy link
Contributor Author

+1 to reopening this for re-consideration

@soullivaneuh
Copy link
Contributor

Facing the same issue.

Could we please reconsider it?

Or maybe @stof, could you please elaborate a little bit your solution with a sample? I'm not sure to fully understand it.

@soullivaneuh
Copy link
Contributor

soullivaneuh commented Feb 2, 2017

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.

@TerjeBr
Copy link

TerjeBr commented Aug 9, 2017

Can we please reopen this bug?

I have many cases where it would have been good to be able to handle submissions to a form, but then afterwards just clear all form errors.

To change this in the view instead is just a bad work around. This is a vaild change request.
Please reconsider it.

@ouassini
Copy link

ouassini commented Dec 4, 2017

To disable the form validation, you can use the validation_groups property instead of creating new one:

use Symfony\Component\OptionsResolver\OptionsResolver;

public function configureOptions(OptionsResolver $resolver)
{
    $resolver->setDefaults(array(
        'validation_groups' => false,
    ));
}

Documentation https://symfony.com/doc/2.8/form/disabling_validation.html

@TerjeBr
Copy link

TerjeBr commented Dec 5, 2017

@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.

@JarJak
Copy link

JarJak commented Dec 5, 2017 via email

@TerjeBr
Copy link

TerjeBr commented Dec 5, 2017

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.

@TerjeBr
Copy link

TerjeBr commented Dec 5, 2017

@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.

@fabpot fabpot reopened this Dec 5, 2017
@zakarialounes
Copy link

No news about a patch??? @stof, could you please explain your solution? Tks!

@TerjeBr
Copy link

TerjeBr commented Jun 7, 2018

I have many cases where it would have been good to be able to handle submissions to a form, but then afterwards just clear all form errors.

To change this in the view instead is just a bad work around. Please let PR #14233 be merged.

@zakarialounes
Copy link

zakarialounes commented Jun 8, 2018

Sure, but we don't want to write on the vendor class. Any solutions to override the Form class in SF2.7 with an extends or dependency injection?

@JarJak
Copy link

JarJak commented Jun 8, 2018

Since this is about View, not the Form part, I give 👍 for adding FormView::clearErrors. This is not about supressing validation, but not pushing error messages back to the user.

@TerjeBr
Copy link

TerjeBr commented Jun 10, 2018

@JarJak you are right about that "This is not about supressing validation, but not pushing error messages back to the user."

But you are wrong that it can then be done in the FormView. Since the validation errors are stored in the form, the most stright forward implementation is to clear them in the form.

@TerjeBr
Copy link

TerjeBr commented Jun 10, 2018

Symfony maintainers, please have a look at #27571 and see if that fix for this isssue can be merged.

@colinodell
Copy link
Contributor Author

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.

fabpot added a commit that referenced this issue Jun 25, 2018
This PR was squashed before being merged into the 4.2-dev branch (closes #27580).

Discussion
----------

[Form] Add ability to clear form errors

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #14060
| License       | MIT
| Doc PR        | symfony/symfony-docs#9916

This PR adds the ability to manually clear form errors, thus improving the DX issue reported in #14060.

Unlike my original approach in #14233 and #27571 which break BC, this adds a new `ClearableErrorInterface` which `Form` implements.  (`Button` does not implement it because buttons can't have errors.)

Commits
-------

9eb755c [Form] Add ability to clear form errors
@fabpot fabpot closed this as completed Jun 25, 2018
@ampaze
Copy link
Contributor

ampaze commented Jun 25, 2018

Awesome!
Will this also be available for 2.8?

@xabbuh
Copy link
Member

xabbuh commented Jun 25, 2018

@ampaze New features are only added to the upcoming minor version which will be Symfony 4.2 in this case. You can learn more about the release process at http://symfony.com/doc/current/contributing/community/releases.html.

@ampaze
Copy link
Contributor

ampaze commented Jun 27, 2018

@xabbuh I see, too bad, considering that this issue is 3 years old, created when even Symfony 3 wasn't released.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests