4

What is the proper way of emitting an event through a service. I have read that declaring the EventEmmiter in the service it's not suitable.

I want to achieve the following.

I have 2 components inside the root component, when I click in the first component, I want to know that the first component was clicked in the second component.

Tiberius
  • 265
  • 1
  • 6
  • 14
  • Any luck with this? I am also in a asituation where I want to emit event from grandchild component to parent AngularJS application and I thought service is better way to avoid multilevel handshakes. – guravman May 14 '20 at 05:18

2 Answers2

12

There are four possible scenarios in which you can share your data but it depends upon your requirements

Parent to Child: Sharing Data via Input

Child to Parent: Sharing Data via ViewChild

Child to Parent: Sharing Data via Output() and EventEmitter

Unrelated Components: Sharing Data with a Service

When passing data between components that lack a direct connection, such as siblings, grandchildren, etc, you should you a shared service. When you have data that should aways been in sync, I find the RxJS BehaviorSubject very useful in this situation.

You can also use a regular RxJS Subject for sharing data via the service, but here’s why I prefer a BehaviorSubject.

It will always return the current value on subscription - there is no need to call onnext It has a getValue() function to extract the last value as raw data. It ensures that the component always receives the most recent data. In the service, we create a private BehaviorSubject that will hold the current value of the message. We define a currentMessage variable handle this data stream as an observable that will be used by the components. Lastly, we create function that calls next on the BehaviorSubject to change its value.

The parent, child, and sibling components all receive the same treatment. We inject the DataService in the constructor, then subscribe to the currentMessage observable and set its value equal to the message variable.

Now if we create a function in any one of these components that changes the value of the message. when this function is executed the new data it’s automatically broadcast to all other components. Here its a code snippet.

data.service.ts

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

@Injectable()
export class DataService {

  private messageSource = new BehaviorSubject('default message');
  currentMessage = this.messageSource.asObservable();

  constructor() { }

  changeMessage(message: string) {
    this.messageSource.next(message)
  }

}

parent.component.ts

import { Component, OnInit } from '@angular/core';
import { DataService } from "../data.service";

@Component({
  selector: 'app-parent',
  template: `
    {{message}}
  `,
  styleUrls: ['./sibling.component.css']
})
export class ParentComponent implements OnInit {

  message:string;

  constructor(private data: DataService) { }

  ngOnInit() {
    this.data.currentMessage.subscribe(message => this.message = message)
  }

}

second.component.ts

import { Component, OnInit } from '@angular/core';
import { DataService } from "../data.service";

@Component({
  selector: 'app-sibling',
  template: `
    {{message}}
    <button (click)="newMessage()">New Message</button>
  `,
  styleUrls: ['./sibling.component.css']
})
export class SiblingComponent implements OnInit {

  message:string;

  constructor(private data: DataService) { }

  ngOnInit() {
    this.data.currentMessage.subscribe(message => this.message = message)
  }

  newMessage() {
    this.data.changeMessage("Hello from Sibling")
  }

}
Wasiq Muhammad
  • 3,080
  • 3
  • 16
  • 29
  • I have already tried this, but how can I know when the event fires?. I want to fetch an url when I click a button, so I only want to fetch when I click in the component, your example works for passing the value, but if i try to fetch the url I would get an undefined on the component init. – Tiberius Jun 03 '19 at 17:48
  • this is not an answer to your comment but a more general comment - if need to use `BehaviorSubject` in a service check the answer on this question - https://stackoverflow.com/questions/57355066/how-to-implement-behavior-subject-using-service-in-angular-8 – Mauricio Gracia Gutierrez Jul 13 '21 at 03:42
0

I used Replaysubject to notify about changes in data that needed to be updated to the gui in gui-component when the data provided by the service changes.

  1. A service makes a ReplaySubject which holds the data that may change.
  2. All the gui-components / other services subscribe to this object in their init function and get later notified about changes in data. => Do what they need to do.

In my case the service has a polling interval and the data held by it may change without any user actions.

Ville Myrskyneva
  • 1,560
  • 3
  • 20
  • 35