angularrxjsparent-childdata-sharingangular-input

How to share data between (multiple instances) of 2 unrelated components?


I have 2 unrelated components, a search bar and a table. I have a service that shares data between them. The problem is that I have multiple instances of the search + table components depending on the search. Whenever I search, it updates all tables. Is there a way for me to bind one service between 2 components?

I know I can combine both the search and table component into 1 component, but with how my project is set up, it would make things more complicated than it already is.

Without giving out too much, this is what I have.

// Data sharing service
import { Injectable } from "@angular/core";
import { BehaviorSubject } from "rxjs";

@Injectable({
    providedIn: 'root'
})
export class DataSharingService {

    private data = new BehaviorSubject<any>(null);
    currData = this.data.asObservable();

    constructor(){}

    updateSearchRes(newData: any){
        this.data.next(newData);
    }
}
// search component
onClick(e: any){
  // get search data
  this.searchResults = this.getSearchResults(e);

  // send data to service
  this.searchResults.forEach(element => {
     this.dataSharingService.updateSearchRes(element);
  });
}
// table component
ngOnInit(){
  ...

  // listen for data
  this.dataSharingService.currData.subscribe(newSearchRes => {
    this.addDataToTable(data);
  });

  ...
}

Solution

  • You need to use a simple if condition to scope the emits to a single component structure.

    The HTML for the component will look like.

    ...
    <app-search searchKey="home"/>
    ...
    <app-table searchKey="home"/>
    ...
    

    First we convert the service emit method, to emit an object with an event name, instead of just the data.

    // Data sharing service
    import { Injectable } from "@angular/core";
    import { BehaviorSubject } from "rxjs";
    
    @Injectable({
        providedIn: 'root'
    })
    export class DataSharingService {
    
        private data = new BehaviorSubject<any>(null);
        currData = this.data.asObservable();
    
        constructor(){}
    
        updateSearchRes(eventName: string, newData: any){
            this.data.next({eventName, data: newData });
        }
    }
    

    Now When you click on search, you take an @Input() searchKey = '', this searchKey, can be used to filter the emits received by the table.

    // search component
    ...
    @Input() searchKey = ''
    ...
    
    ...
    onClick(e: any){
      // get search data
      this.searchResults = this.getSearchResults(e);
    
      // send data to service
      this.dataSharingService.updateSearchRes(this.searchKey, this.searchResults);
    }
    

    Then use this same approach, to filter on the table.

    // search component
    ...
    @Input() searchKey = '';
    private sub: Subscription = new Subscription();
    ...
    
    ...
    // table component
    ngOnInit(){
      ...
    
      // listen for data
      this.sub.add(
        this.dataSharingService.currData.subscribe((event: any) => {
          if(event.eventName === this.searchKey) {
              this.addDataToTable(event.data);  
          }
        })
      );
    
      ...
    }
    
    ngOnDestroy() {
      this.sub.unsubscribe();
    }