'Angular 2: BehaviorSubject, avoid with multiple subscription

Hi I have following problem in my Angular 2 app.

I have BroadcastService:

@Injectable()
export class BroadcastService {

   private broadcastSubject: BehaviorSubject<Event> = new BehaviorSubject<Event>(0);

   public next(event: Event): void {
     return this.broadcastSubject.next(event);
   }

   public subject(event: Event): Observable<Event> {
     return this.broadcastSubject.asObservable().filter(e => e === event);
   }
 }

Which I use in components like this:

export class MyComponent implements OnInit {

   constructor(public broadcastService: BroadcastService) {
     this.broadcastService.subject(Event.BLA_EVENT).subscribe(() => this.bla());
   }
...

Whenever I route to '/mycomponent' the MyComponent.constructor is called, so the Event.BLA_EVENT is subscriped multiple times.

Any advice how to prevent multiple time subscription?



Solution 1:[1]

I had a similar problem and I solved it by checking the quantity of the observers on the event. The option using ngOnDestroy may be the best, but another approach could be something like:

constructor(public broadcastService: BroadcastService) {
    if(this.broadcastService.subject(Event.BLA_EVENT).observers.length == 0) {
        this.broadcastService.subject(Event.BLA_EVENT).subscribe(() => this.bla());
    }
}

Solution 2:[2]

Now you can use BroadcastChannel in almost all browsers (see caniuse):

import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class BroadcastService {
  private consumer: BroadcastChannel;
  private producer: BroadcastChannel;

  constructor() {
    this.consumer = new BroadcastChannel('pluginGlobalEventBus');
    this.producer = new BroadcastChannel('pluginGlobalEventBus');
  }

  postMessage(message: any): void {
    this.producer.postMessage(message);
  }

  addEventListener(eventName, listener): void {
    this.consumer.addEventListener('message', event => {
      if (event.data.name === eventName) {
        listener(event.data.value);
      }
    });
  }
}

export class EmitEvent {
  constructor(public name: any, public value?: any) {}
}

usage:

// sending
const event = new EmitEvent('logEvent', 'BUTTON_CLICKED');
this.broadcastService.postMessage(event);


// listening
this.broadcastService.addEventListener('logEvent', (interaction: string) => {
  console.log(interaction)
});

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Johna
Solution 2 Ron Jonk