Angular2 - Interaction between components using a service

10,540

Shared service is a common way of communication between non-related components. Your components need to use a single instance of the service, so make sure it's provided at the root level.

An example using the BehaviorSubject as a data delegate:

Shared service:

@Injectable()
export class SharedService {

    isVisibleSource: BehaviorSubject<boolean> = new BehaviorSubject(false);

    constructor() { }
}

Component 1:

export class Component1 {

    isVisible = false;

    constructor(private sharedService: SharedService) { }

    onClick(): void {
        this.isVisible = !this.isVisible;
        this.sharedService.isVisibleSource.next(this.isVisible);
    }
}

Component 2:

export class Component2 {

    constructor(private sharedService: SharedService) { }

    ngOnInit(): void {
        this.sharedService.isVisibleSource.subscribe((isVisible) => {
            console.log('isVisible: ', isVisible); // => true/false
        });
    }
}

It is worth mentioning that BehaviorSubject upon a subscription returns the last value it holds, therefore the component from the example above will be updated with the most recent value immediately after the instantiation.

BehaviorSubject also allows to get its most recent value without even subscribing to it:

this.sharedService.isVisibleSource.getValue(); // => true/false
Share:
10,540
LHIOUI
Author by

LHIOUI

Fullstack javascript engineer experienced on multiple frameworks (reactjs/angular 2-4-5 and 6, nodejs) and mobile applications developer(react-native and native apps). Enthusiastic and collaborative team member always willing to take any tasks to achieve success.

Updated on June 26, 2022

Comments

  • LHIOUI
    LHIOUI almost 2 years

    I have two component A and B, where component A contains a button. I wish when user click on this button, fire a function on component B

    <A></A>
    <router-outlet></router-outlet>
    

    And the component B is rendered using routing.I am considering using a service with an observable boolean that indicate if the button in A is clicked. Is this the right way to achieve it ?

  • foufrix
    foufrix almost 7 years
    Thanks for this, very clean and work out of the box in angular 4 don't forget to import : import {BehaviorSubject} from 'rxjs/BehaviorSubject'; in the service file. Helped me a lot !
  • Shashank Gaurav
    Shashank Gaurav almost 7 years
    But this documentation you posted: angular.io/docs/ts/latest/cookbook/… talks explicitly about 'Parent and children communicate via a service' and not sibling to sibling!.. What if I want something like sibling to sibling communication.. Ex: If A and B are two siblings at same node level..Updating something in A via B and same updating something in B via A..
  • Maxime Gélinas
    Maxime Gélinas almost 7 years
    @ShashankGaurav the documented example show a communication between a parent and a children, but this relationship is not necessary. A service can establish a communication between 2 or more compconents no matter their relationship. See the accepted answer.
  • Skywalker
    Skywalker over 6 years
    Hi Seidme I've tried using the technique you explained above but I keep getting the Expression has changed after it was checked. Previous value: true. Current Value: false error. Do you know what could be causing this?
  • seidme
    seidme over 6 years
    Hi Skywalker, are you using this approach for parent-child component communication? If yes, then the problem might be there, this approach is intended for communication between non-related components Can you please provide plunker? Also, take a look at: stackoverflow.com/questions/34364880/…
  • Skywalker
    Skywalker over 6 years
    @seidme thank you so much for replying. Its not for the parent child approach. Its more of a sibling communication (components of different modules communicating with each other). The only way I could fix the issue was by placing the subscribe and the call to the observable in the components constructor. Im not sure whether thats correct approach. Here's the plunkr. To see the error check the browser console. When the app loads a boolean value of true should be displayed but I get the error in the console.
  • seidme
    seidme over 6 years
    @Skywalker, As can be seen from the plunkr provided, you're using Subjectinstead of BehaviorSubject, there is difference in behaving between the two. Try using BehaviorSubject as demonstrated in the answer and see if error persists. Please check: stackoverflow.com/questions/39494058/…
  • Skywalker
    Skywalker over 6 years
    @seidme I have actually tried the BehaviourSubject but unfortunately it gives the same error. I can't seem to figure this out. I've tried almost everything I can find on the net.
  • seidme
    seidme over 6 years
    @Skywalker Interesting, I'm using this implementation across my all projects and didn't encounter such issues. Will try to investigate what's causing your problem.
  • Skywalker
    Skywalker over 6 years
    @seidme thank you so much for your help! I really appreciate it! I've opened up a question here and any help will be greatly appreciated!
  • LHIOUI
    LHIOUI about 4 years
    Thanks for your answer, but can you explain why angular services shouldn't be stateful? I know that they are often stateless but there is no documentation stating that they should be
  • Wildhammer
    Wildhammer about 4 years
    Service is not an Angular term, it's a paradigm in software engineering that Angular uses. Services contain the repeated business logic that does not belong to a certain component (sharing data is not business logic rather a workaround to overcome a programming issue). If you want your service to save its output then you should inject persistence (local storage, db, apollo, etc.) as context to that service. Besides, this variable in your shared service is accessible to any piece of your application without any hierarchical constraints, very similar to global variable pattern.