0

My app contains two components. One gets items from the API and lists them. When an item is clicked, it passes detailed information to the second component that uses a PrimeNg datatable, which then displays it. Because the second component is dependent on the first based on the item selected, how can I send that data to the second component once an item has been clicked?

Currently, I'm using a BehaviorSubject to relay the item details to the second component. The problem is, is that I want to modify or delete some of the items and as far as I know, you can't delete objects from a BehaviourSubject subscription. My thinking is that I should not use a BehaviourSubject and somehow get the item id and in the same method use that id to get the item details?

My service file:

source: Source;
public sourceUpdate = new BehaviorSubject<Source>(this.source);
currentSource = this.sourceUpdate.asObservable();

//BehaviourSubject method
changeSources(sources) {
        this.sourceSUpdate.next(sources);
        console.log(sources);
    }

//Get items
getSources(): Observable<Source[]> {
        let cpHeaders = new Headers({ 'Content-Type': 'application/json' });
        let queryString = '[{"Name": "deleted","Values": ["false"]}';
        let options = new RequestOptions({ headers: cpHeaders});
      return this.http.post('api/V1/Source/QueryDocument', queryString, options)
        .map(data =>  {return this.extractData(data);})
        .catch(this.handleError);
    }
//Get item details by item id
    getStatementsBySource(sourceId?: string): Observable<Statement[]> {
            let cpHeaders = new Headers({ 'Content-Type': 'application/json' });
            // let cpParams = new URLSearchParams();
            let queryString = '[{"Name": "source.sourceId","Values": ["' + sourceId + '"]},{"Name": "deleted", "Values": ["false"]}]';
            console.log(sourceId);
            let options = new RequestOptions({ headers: cpHeaders});
          return this.http.post('api/V1/Statement/QueryDocument', queryString, options)
            .map(data =>  {return this.extractData(data);},(statements => {
                  return statements.map(statement => new Statement(statement));}))
            .catch(this.handleError);
        }  

private extractData(res: Response) {
        let body = res.json();
        console.log(body);
        return body;
    }

    private handleError (error: Response | any) {
        console.error(error.message || error);
        console.log("error");
        return Observable.throw(error.status);
    }

First component:

getSources() {
      this.sourceDisplayService.getSources().subscribe(sources => {this.sources = sources;});
    }
onRowSelect(event) {
this.sourceDisplayService.changeSource(this.source);
}

Second component:

 this.subscription = this.sourceDisplayService.currentSource.subscribe(source => this.source = source);
averroes
  • 171
  • 3
  • 16

1 Answers1

0

At first, I suggest you to store the full api result in your behaviorSubject:

private _sources = new BehaviorSubject<Source[]>([]);
public sources = this._sources.asObservable();

initData() {
  ...
  this.http.post('api/V1/Statement/QueryDocument', queryString, options)
  .subscribe(result => { this._sources.next(result); }
}

Now, to avoid a double subscription, you can bind the content of clicked source as input on the second component. The implementation depends of the hierarchy of your components: is the second component child of the first, or they are both at the same level?

  • In case of same level components: Bind click event on the first to store the source row in a parent variable. Then bind the parent source variable to the second component:

    <component-1 (click)="currSource=source" *ngFor="let source of service.sources | async"></component-1>

    <component-2 [source]="currSource"></component-2>

  • In case of component 2 child of component 1: do the same but store the row source in the component 1 and bind it directly to the child component.

You can now display the content of source without subscribe to an other subject.

Florian D
  • 221
  • 1
  • 5