Search code examples
angulartypescriptangular-materialangular-components

Exchange references between two same-level components


I have two custom-components that are spawned by a common parent with different data as two tabs on mat-tab-group.

<mat-tab-group>
  <mat-tab label="TAB1">
    <ng-template matTabContent>
      <custom-component [data]="tab1data"></custom-component>
    </ng-template>
  </mat-tab>
  <mat-tab label="TAB2">
    <ng-template matTabContent>
      <custom-component [data]="tab2data"></custom-component>
    </ng-template>
  </mat-tab>
</mat-tab-group>

The data is a setter that sets the internal _data and wraps it in MatTableDataSource:

@Input()
set data(val: Data[]) {
    this._data = val;
    this.loadData();
}

loadData(): void {
    this.dataSource = new MatTableDataSource<Data>(this._data);
    this.dataSource.sort = this.sort;
}

I have a situation where actions for component on the first tab should affect data on the other tab. Is there any way to pass component references, so I could change _data and call loadData() from other component?


Solution

  • You can do this with Rxjs observables

    Here is stackblitz example of how to communicate between two components https://stackblitz.com/edit/angular-ivy-myr2kh

    my-service.service.ts

    import { Injectable } from '@angular/core';
    import { Subject } from 'rxjs';
    
    @Injectable()
    export class MyServiceService {
      private dataChangeObservable = new Subject<any>();
        dataChangeStream = this.dataChangeObservable.asObservable();
    
      constructor() { }
    
      emitDataChange() {
        this.dataChangeObservable.next();
      }
    }
    

    ComponentOne.component.ts

    onclickDiv() {
        this.myService.emitDataChange(); // Here you are triggering for change
    }
    

    ComponentTwo.component.ts

    ngOnInit() {
        this.dataChangeSubscription$ = this.myService.dataChangeStream.subscribe(() => {
           this.count++; // Here you will get notified/listener of change
        })
     }
    

    Update

    If you have same component instances, then you have to pass some value to determine on which instance should get updated

    Like

    https://stackblitz.com/edit/angular-ivy-4pznor

    Your parent html

    <app-componentone [name]="'one'"></app-componentone>
    <app-componentone [name]="'two'"></app-componentone>
    

    Here one and two pass as an input to just to identify the instance

    Then your ts

    import { Component, OnInit, Input } from '@angular/core';
    import { MyServiceService } from '../my-service.service';
    
    @Component({
       selector: 'app-componentone',
       templateUrl: './componentone.component.html',
       styleUrls: ['./componentone.component.css']
    })
    export class ComponentoneComponent implements OnInit {
      @Input() name; // it will have instance name
      count = 0;
      constructor(
         private myService: MyServiceService
      ) { }
    
      ngOnInit() {
        this.myService.dataChangeStream.subscribe((value) => { // here we will get to notify which instance should get updated
          if (this.name !== value) { // Here we checking for instance name for updating, if same component instance don't do anything else update
             this.count++;
          }
        })
      }
    
      onclickDiv() {
        // Here I am passing parameter, so if click is triggered from instance one, we have to update other instances, so passing parameter 'one' i.e. name, to avoid updating same component instance
    
        this.myService.emitDataChange(this.name);
      }
    }