angular2-notifications icon indicating copy to clipboard operation
angular2-notifications copied to clipboard

Notifications with custom Angular 2 exception handler

Open randyangeles opened this issue 8 years ago • 16 comments

Hi, just looking for information/help.

I'm trying to use the notification service along with a custom exception handler class that extends Angular 2's ExceptionHandler.

@Injectable()
export class MyExceptionHandler extends ExceptionHandler {

    constructor(private logger : LoggerService, private notificationService : NotificationsService) {
        super(logger);
    }

    call(error, stackTrace = null, reason = null) {
        this.logger.error(error, "Logging the error");
        this.notificationService.error("Notify other components", ExceptionHandler.exceptionToString(error));
    }

}

MyExceptionHandler is provided on bootstrap so I provide the NotificationsService there also.

bootstrap(AppComponent, [
    // Other items...
    LoggerService, // Another custom class
    NotificationsService,
    {provide: ExceptionHandler, useClass: MyExceptionHandler}
]);

However, no notifications are shown when I try to raise an exception in AppComponent. My LoggerService (another custom class) is able to log the exception.

import { Component } from '@angular/core';
import { LoggerService }from './shared/services/logger/logger.service';
import {NotificationsService, SimpleNotificationsComponent} from "angular2-notifications";

@Component({
  selector: 'my-app',
  directives: [SimpleNotificationsComponent],
  template: `
              <h1>My First Angular 2 App</h1> 
              <button (click)="TestError()">Test Error</button>
              <simple-notifications></simple-notifications>
            `
})
export class AppComponent { 
  constructor(private logger : LoggerService, private notificationService : NotificationsService) {
  }
  TestError() {
    throw new Error("Test Error");
  }
}

Just to confirm everything is setup correctly, I can get notifications to show if I replace the line in TestError() with this:

this.notificationService.error("Error Title", "Error Content");

Am I missing something here or approaching this incorrectly?

randyangeles avatar Jul 31 '16 02:07 randyangeles

I think your setup is completely fine. I don't see why the notifications don't show up. Can you try to recreate the problem in a plunker, so we can play around with it?

flauc avatar Jul 31 '16 21:07 flauc

Alright, Plunker link: https://plnkr.co/edit/2jOVWxFZlHN36OcEaEV9

randyangeles avatar Jul 31 '16 23:07 randyangeles

It doesn't just not display the notification. It brakes them completely.

flauc avatar Aug 01 '16 08:08 flauc

But it doesn't throw an error. This behavior is really weird.

flauc avatar Aug 01 '16 09:08 flauc

So I found something interesting. It looks like you can get notifications to show in child components with my setup. The behavior is still weird though because the notifications only show when the 2nd unhandled exception is thrown. Notifications are generated correctly after that but once you close all notifications, they won't show again until the 2nd unhandled exception.

Updated plunker (added a ChildComponent): https://plnkr.co/edit/EQSjvd98SXFrOLLJTQjs

randyangeles avatar Aug 02 '16 00:08 randyangeles

Hi @randyangeles , i'm trying to replicate the custom exception handler using PrimeNG Message and Growl modules and I have the same problem: the growl notification only show when the 2nd exception is thrown.

Any idea?

Thanks.

erlopezh avatar Aug 30 '16 04:08 erlopezh

Hi @erlopezh, didn't have time to do more digging into the behavior I described. I'll see if I can play around with notifications some more next week.

randyangeles avatar Sep 07 '16 15:09 randyangeles

Hi, I had the same problem with the notification only showed when the 2nd unhandled exception was thrown. I decided to try a different approach. Instead of the ExceptionHandler (actually with RC6 it's called ErrorHandler) communicating with the notification service I let the app-component do it. From my ErrorHandler I then call a custom service which the app-component subscribes to. See this answer for how to setup the service: stackoverflow

In short this is my example code: App-component:

public options = {
  position: ["bottom", "right"],
  timeOut: 10000
}
constructor(private mcNotificationService: MCNotificationsService, private notificationService: NotificationsService) {
  this.mcNotificationService.notifications.subscribe(model => this.generateNotification(model));
}
private generateNotification(notificationModel: MCNotificationModel) {
  // TODO: switch on the type
  this.notificationService.error(notificationModel.title, notificationModel.message);
}

Custom Service:

export enum MCNotificationType {Success, Error, Info, Alert}
export interface MCNotificationModel {
  title: string;
  message: string;
  type: MCNotificationType;
}

@Injectable()
export class MCNotificationsService {
  private notificationObserver = new ReplaySubject<NotificationModel>();

  public notifications = this.notificationObserver.asObservable();

  public generateNotification(model: MCNotificationModel) {
    this.notificationObserver.next(model);
  }
}

Calling Class:

constructor(private notifications: MCNotificationsService) {
}

generateError() {
  this.notifications.generateNotification({title:'Title', message:'Message', type:MCNotificationType.Error});
}

MadsenJensen avatar Oct 14 '16 10:10 MadsenJensen

@aReeMJay I tried your approach, and it fixed the problem initially, but if I wait until all of the notifications have disappeared, the same issue happens when more errors occur. The first error does not produce a notification, but the second error causes both notifications to show at the same time.

derekmt12 avatar Oct 17 '16 14:10 derekmt12

Maybe not the best solution, but I was able to get it working by just wrapping the call in a setTimeout.

handleError(error: any) {
   let unwrappedError = error.originalError || error;
   setTimeout(() => {
      this.notificationsService.error('Error', unwrappedError.message);
   },1);
}

derekmt12 avatar Oct 17 '16 16:10 derekmt12

@derekmt12 .. Great Thanks , I tried your approach and it works.

GopiAnnan avatar Nov 19 '16 01:11 GopiAnnan

@derekmt12 : Great .. Thanks... after 3 hours searching how to fix your workaround works like a charm

leomayer avatar May 18 '17 08:05 leomayer

this solution is not working for me with angular 5 version

manoj-tyagi avatar Jan 25 '18 18:01 manoj-tyagi

I'm using a different notification lib but I was facing the same problem. This solution worked for me:

handleError(error: any) {
    const appRef = this.injector.get(ApplicationRef)
    // <--- show notification here
    appRef.tick()
}

britvik avatar Jan 31 '18 10:01 britvik

For me worked only this (run your notification in zone.run()):

handleError(error) { const dialog = this._injector.get(MatDialog); const zone: NgZone = this._injector.get(NgZone); zone.run(() => dialog.open(ModalInformationComponent).componentInstance.message = error.error); }

closirr avatar Jul 02 '18 12:07 closirr

Can confirm NgZone solution works with angular 5.2.9

handleError(error: any): void {
	this.zone.run(() => this.pageBundle.toast.error("Error", error.message || error));
	super.handleError(error);
}

Sky4CE avatar Jul 02 '18 17:07 Sky4CE