menu

Questions & Answers

An notification doesn't display without window click

I made GlobalHandlerError service. Errors good display in console, however when i added angular-notifier- notifications display only after mouse click in current window(for every notification need one click even if we have some notification together). I tried test angular-notifier in typical component- all well work, but in my service - don't work. Even if i use that without forEach loop.

import {ErrorHandler, Injectable} from '@angular/core';
import {HttpErrorResponse} from "@angular/common/http";
import {NotifierService} from "angular-notifier";
import {ResponseFromServer} from "../entity/ResponseFromServer";


@Injectable({
 providedIn: 'root'
})
export class GlobalErrorHandlerService implements ErrorHandler{

 errorOnClientSide:Error|undefined
 private readonly notifier: NotifierService
 responseFromServer:ResponseFromServer|undefined


 constructor( notifierService:NotifierService) {
   this.notifier = notifierService;
   }


 handleError(error: any): void {
   if (error instanceof HttpErrorResponse){

     this.responseFromServer=error.error
     if (error.ok){
       this.notifier.notify('success',"Success!")
       console.log("success")
       console.log(this.responseFromServer)
     } else {
       console.log("error from server")
       this.responseFromServer?.errors?.forEach(err=> this.notifier.notify('error',err.toString()))
       console.log("GONE!")
     }



   } else {
     if (error instanceof Error) {
       this.errorOnClientSide=error
       this.notifier.notify('error',this.errorOnClientSide.message)
       console.error("Error on client-side "+error)
      } else {
       console.error("Unknown error:"+error)
     }
   }
 }


}

Comments:
2023-01-07 20:31:09
Can you please check if my solution works as well? (since some people might want to use an alternative to NgZone).
2023-01-07 20:31:09
I think that your solution is work well. I don't doubt in that. I chose this solution, cause it is more optimal and more organic cause not require to set timer. Better say me, can i to set two solution here or only one?
2023-01-07 20:31:09
It's totally okay that you selected the other answer as the accepted one. Yet if there are two alternative working answers, it could be nice if the second answer gets an upvote as well. In this way you can signal to the author and to other users, that the other answer is working as well. In addition, it is also a sign of appreciation ;-).
2023-01-07 20:31:09
I thought a lot about your solution and asked myself. If i'd want to find solution for my question, would i want to compare two or tree solution when I'm wanting rapid solution and have a deadline?? No, i want only one solution. If the solution will not work - I'll come back to the topic and see others solution. Your solution here and all can to see that. If your solution will win by votes - I will change my decide. Thank you for help. Thank you for a variant
2023-01-07 20:31:09
I did some research myself now. I added a brief comparison between setTimeout and NgZone to my answer.
Answers(2) :

I've seen it happen before that if a call is being triggered by something other than an HttpClient method, then it doesn't properly trigger change detection in the subscriber/error handler. You could see if this is the issue by manually triggering change detection using zone.run:

import { ErrorHandler, Injectable, NgZone } from '@angular/core';
import { HttpErrorResponse } from "@angular/common/http";
import { NotifierService } from "angular-notifier";
import { ResponseFromServer } from "../entity/ResponseFromServer";


@Injectable({
   providedIn: 'root'
})
export class GlobalErrorHandlerService implements ErrorHandler {

   errorOnClientSide: Error | undefined
   private readonly notifier: NotifierService
   responseFromServer: ResponseFromServer | undefined


   constructor(notifierService: NotifierService, private zone: NgZone) {
       this.notifier = notifierService;
   }


   handleError(error: any): void {
       this.zone.run(() => {
           if (error instanceof HttpErrorResponse) {

               this.responseFromServer = error.error
               if (error.ok) {
                   this.notifier.notify('success', "Success!")
                   console.log("success")
                   console.log(this.responseFromServer)
               } else {
                   console.log("error from server")
                   this.responseFromServer?.errors?.forEach(err => this.notifier.notify('error', err.toString()))
                   console.log("GONE!")
               }
           } else {
               if (error instanceof Error) {
                   this.errorOnClientSide = error
                   this.notifier.notify('error', this.errorOnClientSide.message)
                   console.error("Error on client-side " + error)
               } else {
                   console.error("Unknown error:" + error)
               }
           }
       });
   }
}

I recently had a similar issue and eventually resolved it by wrapping the notify()-call in a timeout. I.e. I now make the call asynchronously:

setTimeout(() =>{
    this.notifier.notify('error', 'Test-Message');
}, 0);

Explanation:

If the call to this.notifier.notify() is made synchronously, it might be executed before Angular's change detection has a chance to run, which could cause the notification to be missed.

EDIT:

A note of caution: Even though my solution is working, I later found out that there is a catch which might make the use of NgZone.run preferable in many situations. setTimeout does not provide the same error handling capabilities as NgZone.run. If an exception is thrown inside the callback function, it will not be caught by Angular's error handling mechanism, and it could potentially cause your application to crash.

Bottom-line: When you just need to display a notification, setTimeout might be acceptable, yet you should rather not change the application state inside a setTimeout.