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

[LiveComponent] How do I manually add a FormError to the Form when my submit-function fails? #1981

Open
Mauriceu opened this issue Jul 12, 2024 · 5 comments

Comments

@Mauriceu
Copy link

Mauriceu commented Jul 12, 2024

Let me preface by saying "yea I know this aint probably the right place to ask", but I am fairly certain this problem is unwanted behaviour...but I'd be glad to be proven wrong.

The LiveComponent would be pretty simple:

#[AsLiveComponent(template: 'some_template.html.twig')]
class SomeLiveComponent extends AbstractController
{
     use DefaultActionTrait, ComponentWithFormTrait;
    
     protected function instantiateForm(): FormInterface
     {
          return $this-createForm(SomeFormType::class);
     }

     #[LiveAction]
     public function save(): ?Response
     {
          try {
               /// ... do some persistence 
          } catch (\Throwable $e) {
               $error = new FormError('An error occurred');
               $this->form->addError($error); // <--- How would I display this error in my template?
          }
     }

The template, as simple as can be:

{# some_template.html.twig #}

{{ form_start(form, 
     {
          'attr': {
               'data-action': 'live#action:prevent',
               'data-live-action-param': 'save'
          }
     })
}}

{{ form_errors(form) }} <!-- The Error should be displayed here -->
{{ form_row(form.someField) }}

<button type="submit" data-loading="action(save)|addAttribute(disabled)">Submit</button>

{{ form_end(form) }}

A somewhat ugly workaround would be adding a $formErrors array property to the component and populating it with whatever error-messages occur. You could then simply display all those messages in the template, though due to how detached that is from the normal symfony form workflow I would rather avoid this.

I understand that throwing a UnprocessableEntityHttpException() makes turbo re-render the HTML it receives in the response, so the "save()" function should throw that whenever it fails. But the errors are not added to the response HTML anyway - either because the HTML has already been rendered or the FormErrors are removed manually...though I have no idea

I tried adding a writable LiveProp array property to my component which would be populated with FormErrors throughout the submit process - similar to the hacky $formErrors array property mentioned above. But the "instantiateForm()" function would add these errors manually to the form - but that does not work either.

Any pointers would be helpful.

@WebMamba
Copy link
Collaborator

Sorry I don't get what you are trying to do here... What error are you trying to catch ? Form errors happen when the value submitted by the user are not valid but no errors are throw the errors are just added to the form. If the error came from outside the form it's a good idea to have an another props for the error outside the form.

@Mauriceu
Copy link
Author

Mauriceu commented Jul 13, 2024

The Idea is that apart from FormErrors being added by Constraints, FormErrors would also be added manually by the Component.

For example, a User submits some data and this data - if valid from a constraint perspective - is then sent to a third party which validates it against some internal constraints and saves it. If this third party rejects the data, or an error occurs in transit, or while saving it, I would like the Form to reflect that by manually adding a FormError.

To expand on the example above:

     #[LiveAction]
     public function save(): ?Response
     {
          /** @var SomeModel $model */
          $model = $this->getForm()->getData();
          try {
               $this->someService->save($model);
          } catch (\Throwable $e) {

               // Possible error cases would be:
               // An error occurred during transit
               // Third party server is not available

               // manually add an error to the form which would then be rendered by the 
               // "form_errors(form)" twig function
               $error = new FormError('An error occurred while saving your data');
               $this->form->addError($error);
          }
     }

If this is currently not possible is it worth thinking about opening a feature request? Otherwise, the FormInterface::addError() function would be obsolete within LiveComponents.

@WebMamba
Copy link
Collaborator

Ok now I got it now, thanks! You can't add Errors after your form have been submitted and validated. This not about LiveComponent, but more about how Symfony form works. So I think you have two options:

  • You can create a custom constraint that do the check with your third party, and then add this custom constraint to your form (https://symfony.com/doc/current/validation/custom_constraint.html)
  • You choose to validate your data after the form is submitted, but then you need to store the error message on a new propertie of your component and not on the form

@Mauriceu
Copy link
Author

Yea I thought about these too, however they would still feel like a workaround, because even when adding the FormError to the form within the "instantiateForm()" function it is not rendered - and at this point the form is not submitted.

@smnandre
Copy link
Member

Only errors related to validated field / models are kept (see: https://github.com/symfony/ux-live-component/blob/2.x/src/ComponentWithFormTrait.php), and the live props are the form values, not the form itself.

Not sure at all, but maybe you can call resetForm and then add some error ?

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

4 participants