flauc / angular2-notifications

A light and easy to use notifications library for Angular.
https://stackblitz.com/edit/angular2-notifications-example
MIT License
746 stars 164 forks source link

Notifications with custom Angular 2 exception handler #53

Open randyangeles opened 8 years ago

randyangeles commented 8 years ago

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?

flauc commented 8 years ago

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?

randyangeles commented 8 years ago

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

flauc commented 8 years ago

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

flauc commented 8 years ago

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

randyangeles commented 8 years ago

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

erlopezh commented 8 years ago

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.

randyangeles commented 8 years ago

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.

MadsenJensen commented 8 years ago

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});
}
derekmt12 commented 8 years ago

@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 commented 8 years ago

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);
}
GopiAnnan commented 7 years ago

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

leomayer commented 7 years ago

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

manoj-tyagi commented 6 years ago

this solution is not working for me with angular 5 version

britvik commented 6 years ago

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()
}
closirr commented 6 years ago

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); }

Sky4CE commented 6 years ago

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);
}