0

So I am trying to connect two components through a service, LoaderComponent and AppComponent with LoaderService, so that when app fetches data a loader shows up. But when I try to use an EventEmitter to emit changes to the components they don't get the changes but when the service subscribes to itself, it can get the changes

LoaderService.ts

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

@Injectable()

class LoaderService {
  @Output change: EventEmitter<number> = new EventEmitter<number>();
  private state: number = 0;

  constructor() { 
     this.change.subscribe(state => console.log(state)); 
     this.setState(1) 
  }

  setState(state: number) {
     this.state = state;
     this.change.emit(this.state);
  }
}
// Shows state when  but outside of the service event is not detected, also tried EventEmitter from from events

I expect to get events from the LoaderService to subscribers

Libby Lebyane
  • 167
  • 2
  • 14
  • 1
    so the typo on `this.change.subsribe(state => console.log(state)); ` has nothing to do with it, right? Also, output is `@Output()` – rmjoia Apr 30 '19 at 18:51
  • Sorry, typed it wrong but in the file it's okay – Libby Lebyane Apr 30 '19 at 18:55
  • t's really good in this situations to setup some sort of example.. working example.. you can easily do something at [Stackblitz](https://stackblitz.com) – rmjoia Apr 30 '19 at 18:57
  • 1
    1. Don't use event emitters or outputs in a service context, use rxjs subjects or behaviorsubjects, Outputs and EventEmitters are specifically for component events and template bindings 2. how/where is this service being provided? check this link for more on point 1: https://stackoverflow.com/questions/36076700/what-is-the-proper-use-of-an-eventemitter#answer-36076701 – bryan60 Apr 30 '19 at 18:58
  • @LibbyLebyane I would consider *bryan60* remark.. but.. I don't see any issues here [stackblitz example](https://stackblitz.com/edit/angular-so-gd7tqp) – rmjoia Apr 30 '19 at 19:07

2 Answers2

1

First thing, EventEmitters and Outputs don't belong in a service.

I will refactor to use subjects and also protect your subject by making it private and exposing a public observable, this limits how your subject state can be modified, this is not required but is generally considered good practice:

import { Injectable } from '@angular/core';
import {Subject} from 'rxjs/Subject';
import {Observable} from 'rxjs/Observable';

@Injectable()

class LoaderService {
  private change: Subject<number> = new Subject<number>();
  change$: Observable<number> = this.change.asObservable();
  private state: number = 0;

  constructor() { 
     this.change$.subscribe(state => console.log(state)); 
     this.setState(1) 
  }

  setState(state: number) {
     this.state = state;
     this.change.next(this.state);
  }
}

Second, this is likely an issue with how you provide your service. If you have an app component template like:

<loader-component></loader-component>
<loader-component></loader-component>

with 2 loader components side by side, and the loader component has a providers array like:

providers: [LoaderService]

then these 2 loaders are receiving different copies of the same service as they each provide and inject their own, so they will not see each other's events.

To remedy this, you provide instead in the app component (and not in the loader component) so they have the same copy of the service, because then the parent is providing the service that each of them inject. If you were to provide in both app component and loader component, they would all receive a different copy.

If you provide at root (module level) then every component that injects the service (and doesn't provide it's own) will receive the same copy of that service.

The appropriate place to provide a service is dependent on your app's needs and the function of the particular service.

bryan60
  • 28,215
  • 4
  • 48
  • 65
1

You need to use LoaderService in some component for angular to create it, if we do not use the service any where angular will automatically discard it. Inject LoaderService in app component like below:

constructor(private _loadService: LoaderService) {} and then you will see the console.log().

Also, it is recommended to use either Subject or Behavior Subject from Rxjs instead of Output in a service.

Anthony
  • 3,595
  • 2
  • 29
  • 38
Kaushik
  • 114
  • 2