symfony / ux

Symfony UX initiative: a JavaScript ecosystem for Symfony
https://ux.symfony.com/
MIT License
820 stars 297 forks source link

[LiveComponent] Idea - Display flash message on prod env when ajax error #807

Closed bastien70 closed 4 months ago

bastien70 commented 1 year ago

Hey, I notified two problems I have :

In dev, when we have an Ajax request which causes an error, we have a modal which is displayed on the screen with inside, Symfony which informs us about the error, as we would have with a request which is not with Ajax.

The problem is that in production environment, well it's the same, except that necessarily, instead of having Symfony informing us about the error (since we are in production), we have in the modal the 404/500/... error page

Maybe it would be a good idea (and I don't know if it's easily doable) to check the environment, and if we're in production, then don't show a modal with the 404/ 500/... but rather a bootstrap modal / sweetAlert or whatever, or a flash message, stating that an error has occurred, without displaying the 404/500/... error page?

Another example. Imagine, a user is on a page that contains a dynamic form, with lots of Ajax requests. If the user leaves the tab aside and comes back to it 30 minutes later to continue typing in the form, inevitably, at the 1st Ajax request, we will have an error which will be

image

In this specific case, it would be interesting to send a flash message (or other) with the appropriate message (which will therefore be taken from the translation files) telling the user that he must refresh the page and start typing again.

What do you think ?

weaverryan commented 1 year ago

Hi!

I'm open to exploring this :)

Another example. Imagine, a user is on a page that contains a dynamic form, with lots of Ajax requests. If the user leaves the tab aside and comes back to it 30 minutes later to continue typing in the form, inevitably, at the 1st Ajax request, we will have an error which will be

Why would this cause this UnprocessableEntityHttpException error?

In general, the thinking is that, except for extraordinary situations, you should never get the error modal on production - just like how the user should never see your 500 page. And so, in the very rare circumstances when this does happen, we're not overly concerned about the UX.

In this specific case, it would be interesting to send a flash message (or other) with the appropriate message (which will therefore be taken from the translation files) telling the user that he must refresh the page and start typing again.

Showing a SweetAlert or flash message of some sort is probably not feasible since they probably won't have SweetAlert and a flash message requires a refresh to see it. The modal works because we hardcode that right into LiveComponents itself.

However, customizing the message that the user sees is feasible. A super simple solution could be to, in the prod environment, instead of showing the 500/whatever page that's returned from the server, we show a generic. styled message - something similar to what you suggested - "telling the user that he must refresh the page and start typing again". We could even make this customizable - e.g. you are able to customize your "500 error for live components" template in the same way that you can customize your global "500 error" template. That would be accomplished by adding an event listener to the KernelException event and, if we're currently rendering a live component & we are about to return an error page, replace the error HTML with the rendering of some other template.

If someone wants this, you can definitely send a PR for it :)

bastien70 commented 1 year ago

Hello!

Why would this cause this UnprocessableEntityHttpException error?

It's a good question, I admit I don't understand why I have this error from time to time after a long period of not having filled in the form. I will monitor this and do some tests.

However, customizing the message that the user sees is feasible. A super simple solution could be to, in the prod environment, instead of showing the 500/whatever page that's returned from the server, we show a generic. styled message - something similar to what you suggested - "telling the user that he must refresh the page and start typing again". We could even make this customizable - e.g. you are able to customize your "500 error for live components" template in the same way that you can customize your global "500 error" template. That would be accomplished by adding an event listener to the KernelException event and, if we're currently rendering a live component & we are about to return an error page, replace the error HTML with the rendering of some other template.

It's a very good idea!

bastien70 commented 1 year ago

Okay I said nothing for the UnprocessableEntityHttpException error, it's caused by entity validation, my bad.

So no error at this level.

Otherwise the idea to have a personalized message in the modal in preprod remains present :)

stefliekens commented 1 year ago

We experience the same problem/situation as @bastien70 described in the ticket. We are now handling this ourselves for each live form component by doing something like below.

Component class:

#[LiveProp]
public ?string $errorMessage = null;

#[LiveAction]
public function submitAction(): RedirectResponse
{
    try {
        $this->errorMessage = null;
        $this->submitForm();
        // ... doing something with the data

        return $this->redirectToRoute('...');
    } catch (UnprocessableEntityHttpException $e) {
        throw $e;
    } catch (\Throwable $e) {
        $this->errorMessage = '...';
        $this->logger->critical('...');
        throw $e;
    }

    throw new UnprocessableEntityHttpException();
}

Twig file:

{{ form(form) }}
{% if errorMessage %}
   {{ errorMessage|trans }}
{% endif %}

As you can see in our case, we show the message below the form. Actually, we are also looking for a way to showing this errors as a toast/flash message, in a general way. We already found in the documentation that it's possible to register a response:error js hook for components individually. But we are looking for a general approach were we can globally register a js hook/plugin for all this situations.

Is this something we can do already? Or maybe you guys can point us in the right direction or share your ideas.

weaverryan commented 1 year ago

Hey @stefliekens!

Actually, we are also looking for a way to showing this errors as a toast/flash message, in a general way. We already found in the documentation that it's possible to register a response:error js hook for components individually

Yup, I think I understand: you need some central code that is aware of / notified when ANY live component has an error. To do this, you could register an event listener on body (e.g. have a Stimulus controller attached to body) that listens to the live:connect event. It looks like this got "kicked out" of the documentation when the hook system was still added, but I guess it still has its use-cases.

Anyway, this listener would be called each time a live component is added to the page and the component should be available via event.detail.component, which you can then use to register the response:error hook.

Let me know if that works - having this use-case in the docs would be fantastic. Or, we can make it even easier, let's do that. The use-case makes sense 👍

stefliekens commented 1 year ago

hi @weaverryan, thanks for your response and suggestion ! I quickly tried it and this works perfect. With this approach we can set up something generic in our application.

Below the POC code I've used:

import { Controller } from "@hotwired/stimulus"
import {toast} from "../utilities/toastr/toastr";

export default class extends Controller {

    connect() {
        this.element.addEventListener('live:connect', (event: Event) => this.registerErrorHandler(event.detail.component));
    }

    registerErrorHandler(component: Component) {
        component.on('response:error', (backendResponse: BackendResponse, controls:  { displayError: boolean}) => {
            toast('danger', '<p>Something went wrong, try again later or please contact us.</p>');
            controls.displayError = false;
        });
    }
}
carsonbot commented 5 months ago

Thank you for this issue. There has not been a lot of activity here for a while. Has this been resolved?

carsonbot commented 4 months ago

Hello? This issue is about to be closed if nobody replies.

stefliekens commented 4 months ago

Hi @carsonbot, we have implemented a custom listener based on the suggestions we received in the comment. You can close the issue.