0

I have a service that provides realtime data to multiple listening components. There is only one instance of the service, and it stores its listeners internally using a ListenerCollection class.

@Injectable()
export class SomeService {
  public listeners: ListenerCollection = new ListenerCollection();
}

The ListenerCollection holds a set of listeners internally. It has multiple usages, one of them being this specific service.

export class ListenerCollection {
  private listeners: Set<any> = new Set<any>();

  constructor() {}

  public addListener(listener) {
    this.listeners.add(listener);
  }

  public removeListener(listener) {
    this.listeners.delete(listener);
  }

  public notifyListeners(param: any) {
    const listeners: Iterable<any> = this.listeners.values();
    for (const listener of listeners) {
      listener(param);
    }
  }
}

Now, it works excellent. But I don't like the fact that I have to create a custom class to store and notify the listeners. My gut feeling tells me that something already exists for this.

So, this got me wondering, what would be the Angular way of doing this?

bvdb
  • 22,839
  • 10
  • 110
  • 123
  • Possible duplicate of [How to subscribe to an event on a service in Angular2?](https://stackoverflow.com/questions/31131490/how-to-subscribe-to-an-event-on-a-service-in-angular2) – Igor Jan 14 '19 at 14:15
  • You can use Subject or SubjectBehavior as Observable from RxJs and then subscribe to them from your listeners . They are a Observer pattern representation at high level. – Keryanie Jan 14 '19 at 14:15
  • @CoveredEe could you elaborate a bit about the difference between these objects. - I have no problems to get it to work. My problem is more about - what is the state-of-the-art way of doing it, and what pitfalls can I expect. - Should I read a book about RxJs ? – bvdb Jan 16 '19 at 16:50
  • 1
    @bvdb Before you go and read a whole book, go through this [Angular RxJs guide](https://angular.io/guide/rx-library) – Jonas Praem Jan 16 '19 at 19:05

1 Answers1

2

This seems like a small question, but it is actually really broad. You need to use Subscriptions to subscribe to a BehaviourSubject as an Observable. Here is a very stripped example of that:

@Injectable()
export class SomeService implements OnDestroy {
    private isReady = new BehaviorSubject<boolean>(false); // type: boolean - Default value: false

    public get isReady$() {
        return this.isReady.asObservable(); // returns BehaviourSubject as an Obervable
    }

    ngOnDestroy() {
        this.isReady.complete(); // Good practice to complete subject on lifecycle hook ngOnDestroy
    }
}

In your component you subscribe to changes

export class SomeComponent implements OnInit, OnDestroy {

  constructor(private someStateService: SomeService) {} // Inject service in constructor

  public ngOnInit(): void {
    this.isReadySubscription = this.navigationStateService.getMenuBackgroundColor$().subscribe(data => {
            // change callback
            console.log(data);
        });

  public ngOnDestroy(): void {
   // Remember to unsubscribe your services on destroy - otherwise you might have memory leaks
   if (this.someStateService) {
      this.someStateService.unsubscribe();
   }
  }
}

Now while this is not the only way of doing it, this is the 1.01 lesson in services. Look into the docs for more information about Observables. If you are making calls to an API, look into the http client

Jonas Praem
  • 2,296
  • 5
  • 32
  • 53