0

I need to share a property which is a number between two components that aren´t child/parent relationed. I need a true example of how to do it. For now, the property is only in one component, so I also need to know how to send data between then. I will not use things like click, I want to know if I can send the change when a function changes the value;

for example:

component A has a number (monthValue) - component A has a function

setMonthValue(){
    this.monthValue = x;
}

How can both components (A e B) share this value? I need to receive/read this value in B;

  • I would use a `Subject` in the service. Check out `dudi`'s answer here: https://stackoverflow.com/questions/35884451/angular-2-sibling-component-communication – AliF50 Apr 14 '20 at 17:53

2 Answers2

1

Indeed, the best approach will be that of service. Before giving you a sample code, I would like you to be careful on one point.

A component should not have a "business code".

From this information, it is easier to architect its application. This famous "business code" will therefore be contained in a service. Know that observables can help you!

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

@Injectable({
  providedIn: 'root'
})
export class BusinessService
{
  public mySharedData$: BehaviorSubject<Date> = new BehaviorSubject<Date>(new Date());
}

This service makes it possible to contain logic, business information and to make it possible to synchronize several components which are not parent / child. It is important to note that this service is a singleton. You should not add it in the providers table of a module. If you do this, know that each component will have its own instance of this service and the result will not be there!


Component A:

import { Component, OnDestroy, OnInit } from '@angular/core';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';

import { BusinessService } from './../business.service';

@Component({
  selector: 'app-component-a',
  templateUrl: './component-a.component.html',
  styleUrls: ['./component-a.component.css']
})
export class ComponentAComponent implements OnInit, OnDestroy
{
  public data: Date;

  private _destroy$: Subject<boolean> = new Subject<boolean>();

  public constructor(
    private _businessService: BusinessService
  ) { }

  public ngOnInit(): void
  {
    this._businessService
      .mySharedData$
      .pipe(takeUntil(this._destroy$))
      .subscribe(value => this.data = value);
  }

  public ngOnDestroy(): void
  {
    this._destroy$.next(true);
    this._destroy$.complete();
  }
}

Component B:

import { Component, OnDestroy, OnInit } from '@angular/core';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';

import { BusinessService } from './../business.service';

@Component({
  selector: 'app-component-b',
  templateUrl: './component-b.component.html',
  styleUrls: ['./component-b.component.css']
})
export class ComponentBComponent implements OnInit, OnDestroy
{
  public data: Date;

  private _destroy$: Subject<boolean> = new Subject<boolean>();

  public constructor(
    private _businessService: BusinessService
  ) { }

  public ngOnInit(): void
  {
    this._businessService
      .mySharedData$
      .pipe(takeUntil(this._destroy$))
      .subscribe(value => this.data = value);
  }

  public ngOnDestroy(): void
  {
    this._destroy$.next(true);
    this._destroy$.complete();
  }
}

This is the code for component A and B. We inject the BusinessService into the various components and we subscribe to the value changes of the mySharedData property present in the BusinessService via the ngOnInit method. The mechanism is as follows:

  • The service is in memory at the time of injection
  • The service values ​​the mySharedData property with today's date and notifies the subscribers of the change (The different components).
  • The different components receive the update and process the data as they want.

At this point, you can synchronize a value between several components. You still have to understand the update mechanism. Indeed, you would for example want to update this value from a component.

Let's modify the code of component B a bit:

import { Component, OnDestroy, OnInit } from '@angular/core';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';

import { BusinessService } from './../business.service';

@Component({
  selector: 'app-component-b',
  templateUrl: './component-b.component.html',
  styleUrls: ['./component-b.component.css']
})
export class ComponentBComponent implements OnInit, OnDestroy
{
  public data: Date;

  private _destroy$: Subject<boolean> = new Subject<boolean>();

  public constructor(
    private _businessService: BusinessService
  ) { }

  public ngOnInit(): void
  {
    this._businessService
      .mySharedData$
      .pipe(takeUntil(this._destroy$))
      .subscribe(value => this.data = value);
  }

  public ngOnDestroy(): void
  {
    this._destroy$.next(true);
    this._destroy$.complete();
  }

  public updateData(): void
  {
    this._businessService
      .mySharedData$
      .next(new Date());
  }
}

That's all ! A new value will be pushed into the service and the different components will be notified of the change.

I give you a concept, it's up to you to work on it. Note that it is possible to use the async pipe to directly manage the subscription to mySharedData in your component templates.

I remain of course available if you have any questions and / or comments.

Note: To have a clean code, I advise you to clean the subscriptions to the destruction of the component. I gave you an example along these lines to give you ideas. (_destroy$ + pipe + takeUntil + ngOnDestroy).

See you soon and good luck!

jboileau173
  • 181
  • 2
0

One possible way is to use a BehaviorSubject in a shared service.

private monthValueSubject = new BehaviorSubject<number>(-1);

The good thing about a shared service's BehaviorSubject is that it will retain it's value so any time a new subscription is made it will get the latest value.

import { Injectable } from "@angular/core";
import { Observable, BehaviorSubject } from "rxjs";

@Injectable()
export class SharedService {
  private monthValueSubject = new BehaviorSubject<number>(-1);

  constructor() {}

  setMonthValue(x: number) {
    this.monthValueSubject.next(x);
  }

  getMonthValueObservable(): Observable<number> {
    return this.monthValueSubject.asObservable();
  }
}

This simple service can have two methods that will let you update the month value.

Inside your components two basic things are required.

  ngOnInit() {
    this.sharedService.getMonthValueObservable().subscribe(x => {
      this.monthValue = x; // <-- this is where you get new values
    });
  }

  setMonthValue(x: number) {
    this.sharedService.setMonthValue(x); // <-- this is where you set new values
  }

Check this working stackblitz.

robert
  • 5,742
  • 7
  • 28
  • 37