Angular2 how to update an element inside a monitored collection

Update, in short:

I am looking for the equivalent of doing something similar, but for an observable, not a regular array:

var i = this.customers.findIndex(customer => customer._id === id); ~i && this.customers[i] = newObject. 

I have 2 components on the screen. The list component on the left and the display component on the right (imagine this is a PDF file that simply displays the latest "version" of data)

When you click an item in the list, it displays the data for that selected item in the component on the right.

The list is an observable array:

 items$: Observable<Proposal[]>; 

Each list item has a child component. You can click the icon on one item that changes the data of this child. The child has an event emitter to inform parents that the data has changed:

 @Output() proposalDataChanged: EventEmitter<string> = new EventEmitter(); 

The parent becomes attached to him:

  <fb-proposal-list-item [proposal]="proposal" (proposalDataChanged)="handleDataChanged(p)"> </fb-proposal-list-item> 

The problem is that in the handleDataChanged method I want to do an Observable search for the changed item and replace it with the new payload returned by the emitter. I do not want to call the server to update the entire list.

I need to do this so that the component on the right reflects the new data.

I can find the element as follows:

 handleDataChanged(data: Proposal){ this.items$.subscribe((items: Proposal[]) => item = items.find(p => p.id == data.id)); } 

but can't figure out how to update an item in Observable, and not just find the one that has changed.

I know that I can "trick" the component by moving to another place and then return again to make it update, but it also gets into the API (and reloads the page).

The url is as follows:

 /pages/proposals/manage/-XHzOJY/document 

This pool in the URL is the identifier of the currently selected item (which is displayed in the component on the right).

So I can’t use params change detection here because it does not change. The user causes a change to the already selected object, which is one of many inside the observed array.

UPDATE

Here is the complete code for the parent component:

 import { Component, OnInit } from '@angular/core'; import { Router } from '@angular/router'; import { Observable, Subject } from 'rxjs/Rx'; import { Proposal } from '../proposal'; import { ProposalService } from '../proposal.service'; import { SearchService } from '../../services/search-service'; @Component({ selector: 'fb-proposal-list', templateUrl: './proposal-list.component.html', styleUrls: ['./proposal-list.component.css'] }) export class ProposalListComponent implements OnInit { total$: Observable<number>; items$: Observable<Proposal[]>; term: string = ""; currentPage: number = 1; private pageStream = new Subject<number>(); constructor( private _searchService: SearchService, private _proposalService: ProposalService, private _router: Router) { } ngOnInit() { this.setupSearching(); // let timer = Observable.timer(0, 60000); // timer.subscribe(() => this.goToPage(this.currentPage)); } setupSearching(){ const searchSource = this._searchService.searchTermStream .map(searchTerm => { this.term = searchTerm; return {search: searchTerm, page: 1} }); const pageSource = this.pageStream.map(pageNumber => { this.currentPage = pageNumber; return {search: this.term, page: pageNumber} }); const source = pageSource .merge(searchSource) .startWith({search: this.term, page: this.currentPage}) .switchMap((params: {search: string, page: number}) => { return this._proposalService.getProposalsPaged(params.search, params.page) }) .share(); this.total$ = source.pluck('meta').pluck('total_items'); this.items$ = source.pluck('items'); } goToPage(page: number) { this.pageStream.next(page) } handleDataChanged(id: string){ this.goToPage(this.currentPage); } } 
+5
source share
2 answers

I don’t think you fully understand how Observables work. What you call the “observable collection” here is not what you can imagine as in the “collection of observable elements”.

This is actually a stream of emitted collections. Therefore, when you have an Observable<Model[]> , this is not a collection observed by some observer, it is actually a stream of emitted Model[] collections. In this sense, you cannot update the emitted value (obviously, since it has already been emitted), but you want the observable to emit a new, updated collection of Model .

Before you try to do this, you will need to know that you cannot emit something from the observable that you yourself did not create. You need a Subject, an Observable, and an Observer (it inherits both the Observer and Observable interfaces).

So let me build something like this:

 subject: Subject<Proposal[]> = new Subject(); _proposals: Proposal[] = []; get proposals() { return this.subject.asObservable(); } 

Let's say you get your suggestions from an API call:

 http.get("...").map(response => response.json().subscribe(data => { this._proposals= <Proposal[]>data; // save your data this.subject.next(this._proposals); // emit your data }); 

And now you want to update your data:

 updateProposals() { this._proposals = this._proposals.filter(...); // or whatever this.subject.next(Object.assign({}, this._proposals)); // emit completely new value } 

This may seem like a lot, and I would recommend learning more about how Observables work for future problems you might have. Greetings.

+11
source

You can do it below.

  • use | async | async pipe
  • create a private variable, and in your subscription block add a value to what it will reflect there.
0
source

Source: https://habr.com/ru/post/1265457/


All Articles