0
votes

I have created a notification service for my application as following :

export class NotificationService {
  private subject = new Subject<Notification>();

  constructor() {}

  getNotification(): Observable<any>{
    return this.subject.asObservable();
  }

  success(title: string, message: string){
    this.notif(NotificationType.SUCCESS, title, message);
  }

  error(title: string, message: string){
    this.notif(NotificationType.ERROR, title, message);
  }

  technicalError(title:string, message: string){
    this.notif(NotificationType.TECHNICAL_ERROR, title, message);
  }

  info(title: string, message: string){
    this.notif(NotificationType.INFO, title, message);
  }

  warning(title: string, message: string){
    this.notif(NotificationType.WARNING, title, message);
  }

  notif(type: NotificationType, title: string, message: string){
    this.subject.next(<Notification>{ type: type, title: title, message: message});
  }

And this is an example on how I use this service:

this.notificationService.success("Suppression", "L'enregistrement a été supprimée !");

And since I have a component which is shared between all my components which is the header, I have a subscribe for the notification service subject in it's ngOnInit function:

this.notificationService.getNotification().subscribe((notification: Notification) => {
      this.notification.create(NotificationType[notification.type], notification.title, notification.message);
    });

For the first time I run the application the code inside subscribe function is executed once when I call some notificationService function, but after that the code inside the subscribe function executes multiple times.

How can I solve this ?

2
How do you share the header component between your other components? - Alex K
@AlexK All my components have this in line in their html code <app-header></app-header> so the header will always appear. - Renaud is Not Bill Gates
Why don't you put the header in the app.component.html, above your router outlet so it is only defined one time for all of the components? - DeborahK

2 Answers

2
votes

Each time an instance of your Header Component is created, it creates another subscription to your Notification Service. When the Header Component is destroyed, it must clean up its subscriptions. If not, the subscribe block will continue to execute whenever the observable emits a new value.

In general, any component which subscribes to a service should contain an ngOnDestroy lifecycle method which cleans up any subscriptions it has made.

To unsubscribe automatically, you can use a componentDestroyed Subject along with the rxjs takeUntil operator. In ngOnDestroy, you emit a value on componentDestroyed which completes the subscription:

export class HeaderComponent implements OnInit, OnDestroy {
  private componentDestroyed = new Subject<any>();

  constructor(private notificationService: NotificationService) { }

  ngOnInit() {
    this.notificationService.getNotification()
      .pipe(takeUntil(this.componentDestroyed))
      .subscribe(notification => {
        // ...
      });
  }

  ngOnDestroy() {
    this.componentDestroyed.next();
  }
}

Here's a StackBlitz example.

0
votes

You're calling the notif method from other methods like success, error, info, warning etc. The notif method is calling next on your Subject which eventually is pushing a new value to the Subject that you have exposed as an Observable by calling the asObservable() method on it.

Make sure that you're not calling the success, error, info, warning etc. anywhere else in your application. If these methods are called more than once, the subscribe method will also get called more than once.