web-dev-qa-db-fra.com

Angular2 comment mettre à jour un élément dans une collection observable

Mise à jour, en bref:

Je cherche l'équivalent de faire quelque chose comme ceci, mais pour un tableau observable plutôt qu'un tableau régulier:

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

J'ai 2 composants sur l'écran. Un composant de liste à gauche et un composant d'affichage à droite (imaginez qu'il s'agisse d'un PDF qui ne fait que restituer la dernière version des données)

Lorsque vous cliquez sur un élément de la liste, les données correspondant à cet élément sélectionné sont affichées dans le composant de droite.

La liste est un tableau observable:

items$: Observable<Proposal[]>;

Chaque élément de la liste a un composant enfant. Il est possible de cliquer sur une icône sur un seul élément, ce qui modifie les données de cet enfant. L'enfant a un émetteur d'événement pour indiquer au parent que les données ont été modifiées:

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

Le parent s'y lie:

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

Le problème que j'ai est que, dans la méthode handleDataChanged, je souhaite rechercher dans l'observable l'élément modifié et le remplacer par le nouveau contenu renvoyé par l'émetteur. Je ne veux pas appeler le serveur pour actualiser la liste complète.

Je dois le faire pour que le composant de droite reflète les nouvelles données.

Je suis capable de trouver l'article comme ceci:

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

mais ne peut pas comprendre comment mettre à jour l'élément dans l'observable, plutôt que de trouver celui qui a changé.

Je sais que je peux «tromper» le composant en naviguant ailleurs, puis de nouveau pour le forcer à se rafraîchir, mais cela frappe également l'API (et recharge la page également).

L'URL ressemble à ceci:

/pages/proposals/manage/-XHzOJY/document

Ce slug dans l'URL est l'id de l'élément actuellement sélectionné (qui est rendu dans le composant de droite).

Je ne peux donc pas utiliser la détection de changement des paramètres ici, car elle ne change pas. L'utilisateur provoque une modification de l'objet déjà sélectionné, qui est l'un des nombreux éléments du tableau observable.

METTRE &AGRAVE; JOUR

Voici le code complet du composant parent:

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);  
  }
}
7
rmcsharry

Je ne pense pas que vous compreniez parfaitement le fonctionnement des observables. Ce que vous appelez ici "collection observable" n'est pas ce que vous pourriez penser, comme dans "collection d'éléments observables".

En réalité, il s’agit d’un flux de collections émises. Ainsi, lorsque vous avez Observable<Model[]>, ce n'est pas une collection observée par un observateur, mais bien un flot de collections Model[] émises. En ce sens, vous ne pouvez pas mettre à jour la valeur émise (bien sûr, comme elle l’a déjà été), mais vous voulez qu’elle soit émise une autre, mise à jour collection de Model.

Avant d'essayer cela, vous devez savoir que vous ne pouvez pas émettre quelque chose d'observable que vous n'avez pas créé vous-même. Ce dont vous avez besoin est un sujet, un objet à la fois observable et observateur (il hérite des interfaces observateur et observable).

Alors, construisons quelque chose comme ça:

subject: Subject<Proposal[]> = new Subject();
_proposals: Proposal[] = [];

get proposals() {
  return this.subject.asObservable();
}

Supposons que vous obteniez vos propositions d’un appel d’API:

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

Et maintenant, vous voulez mettre à jour vos données:

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

Cela peut sembler beaucoup et je recommanderais de lire davantage sur la façon dont les observables fonctionnent pour les futurs problèmes que vous pourriez avoir. À votre santé.

16
Adnan A.

Vous pouvez le faire de différentes manières

  • utiliser | async pipe
  • créez une variable privée et, dans votre bloc subscribe, ajoutez de la valeur à celle qui y apparaît.
1
Aniruddha Das