2

On Angular 6 I have created one service

@Injectable({ providedIn: 'root' })
export class CrossEventService {
  public refresh$: EventEmitter<string> = new EventEmitter<string>();
  public refreshSections(event: any) {
    this.refresh$.emit(event)
  }
}

now from the Other component ngOnint method if that particular method is invoked

invoking method like

this.CrossEventService.refreshSections(""); 

and now I am checking on the other component ngOnInit method

 ngOnInit() {
    this.CrossEventService.refresh$.subscribe(data => {
      if (data) {
        // call whatever method i need to call from my current component
      }
    });

but in some place the above subscribe method is calling multiple times?

James Z
  • 11,838
  • 10
  • 25
  • 41
Jain
  • 45
  • 6
  • Where exactly it is called multiple times? Can you reproduce in a plunker? – Basheer Kharoti Nov 25 '20 at 12:06
  • If you're using `this.CrossEventService.refreshSections("")` in template through property binding or interpolation and if you're using default change detection strategy, it could be triggered for each CD cycle. That in turn will emit a notification to the subscription. – Michael D Nov 25 '20 at 13:05

1 Answers1

2

Avoid EventEmitter in a service:

I am not sure what is your objective but using an EventEmitter in a service is a discouraged practice. Following Angular documentation, EventEmitter should we used with an @Output decorator which has no effect on a service (it is used in children to parent component communication). And in the other side it forces you to expose your emitter.

@Injectable()
export class MyService {
   @Output()   // <<< has no effect
   public events: EventEmitter<any> = new EventEmitter();
   // ^^ makes the emitter public
}

Instead prefer a Subject or BehaviorSubject approach:

As suggested by @Mark Rajcok in another post, prefer any of the following methods:

A Subject is both an Observable (so we can subscribe() to it) and an Observer (so we can call next() on it to emit a new value). We exploit this feature. A Subject allows values to be multicast to many Observers. We don't exploit this feature (we only have one Observer).

BehaviorSubject is a variant of Subject. It has the notion of "the current value". We exploit this: whenever we create an ObservingComponent, it gets the current navigation item value from the BehaviorSubject automatically.

In your case, the service would look as follows:

@Injectable({ providedIn: 'root' })
export class CrossEventService {
  private _refreshSource$ = new Subject<any>();
  public refreshStream$ = this._refreshSource$.asObservable();
  public refreshSections(event: any) {
    this._refreshSource$.next(event)
  }
}

If you wonder why to create a refreshStream$ which takes as source the _refreshSource$, it's because it is useful when we like to expose the data from the subject, but at the same time prevent new data pushed back into the subject.

So that's why you then need to trigger the refreshSections like you are doing from a component:

this.CrossEventService.refreshSections(""); 

But then subscribe to refreshStream$. Bear in mind all components subscribed to refreshStream will get activated and passed in the new value of the subject:

import {Subscription} from 'rxjs/Subscription';

export class ObservingComponent {
  subscription: Subscription;
  event: any;
  constructor(private _crossEventService:CrossEventService) {}
  ngOnInit() {
    this.subscription = this._crossEventService.refreshStream$
       .subscribe(event => this.event = event)
  }
  ngOnDestroy() {
    // prevent memory leak when component is destroyed
    this.subscription.unsubscribe();
  }
}

Notice I've added a subscription from RxJs in order to unsubscribe from the subject when the component is destroyed. Also have a clear idea on what information you want to pass to the .next({information}) as you can probably skip the event syntax you were inheriting from EventEmitter.

Dharman
  • 21,838
  • 18
  • 57
  • 107