web-dev-qa-db-fra.com

Angular 2 façons d'obtenir un article de Observable <Xyz []>

Étant donné le TypeScript suivant dans un service Angular 2:

getLanguages () {
    return this.http.get(this._languagesUrl)
        .map(res => <Language[]> res.json().data)
        .catch(this.handleError);

J'ai des difficultés à utiliser ceci dans des circonstances où je dois rechercher un élément spécifique dans le tableau. Par exemple, je ne peux pas faire ce qui suit car filter attend un Observable<Language> plutôt qu'un Observable<Language[]> qui est renvoyé.

getLanguages().filter(language => language.id == 3) // Error

Je comprends que mon problème est peut-être que je mélange les comportements synchrone et asynchrone. Je vous présente donc mon cas d'utilisation: l'utilisateur peut entrer un ID de langue et je souhaite afficher le nom de langue associé. Je souhaite utiliser getLanguages() avec le résultat Observable car il est déjà utilisé ailleurs dans le projet. Je souhaite également implémenter une certaine mise en cache afin que la demande HTTP ne soit pas effectuée à chaque fois que j'effectue une recherche.

Des pensées?

7
gxclarke

Voici un exemple de travail de ce que vous voulez faire:

https://plnkr.co/edit/lK47pVaW8b0CEez0mum4?p=preview

Appuyez sur la touche F12 chromée pour afficher les journaux afin de vous donner une idée plus précise de ce qui se passe et de la raison pour laquelle cela ne fonctionne pas dans votre exemple.

Portez une attention particulière à:

constructor(private _langService: LanguagesService) {
    _langService.getLanguages() //We get an Observable<Array> object returned.
        //So this is the observable's filter function:
        .filter( this._filter3rdLanguage )
        //The filter gets called only once and its comparing an observable object, not a language object.
        //that's why nothing gets filtered:
        .do( o => console.log(o) )
        //If you filter the actual list instead of the observable object, you'll get it called several times.
        //This is the Array's filter function.
        .subscribe( list => this.languages = list.filter( this._filter3rdLanguage ) );
}

C'est une autre façon, peut-être meilleure, de le faire:

  _langService.getLanguages()
    .map( list => list.filter(this._filter3rdLanguage) )
    //see that this one IS filtered.
    .do( list => console.log(list) )
    .subscribe( list => this.languages = list );
2
Langley

trouver un élément dans Observable <Type>

    let item: Language;
    langugages. // observable cached data
        .subscribe((items: Language[]) => item = items.find(p => p.id == 3));
7
hadi

Vous pouvez utiliser mergeMap au lieu de map pour aplatir le tableau:

getLanguages () {
    return this.http.get(this._languagesUrl)
        .flatMap(res => Rx.Observable.fromArray(<Language[]> res.json().data))
        .catch(this.handleError);

}

Maintenant, la valeur de retour de getLanguages sera un Observable<Language>

2
paulpdaniels

Si vous suivez la méthode map avec subscribe, vous pouvez accéder aux membres du tableau Language lorsque le tableau est disponible:

return this.http.get(this._languagesUrl)
  .map(res => <Language[]> res.json().data)
  .subscribe((languages: Language[]) => alert(languages[3]); );
1
MatthewScarpino

C'est une supposition puisque je n'ai pas utilisé RxJS personnellement, bien que cela m'intéresse. En parcourant la documentation, je me demande si les solutions suivantes fonctionneraient:

function getLanguageName(id: number): Observable<string> {
    return getLanguages()
              .map((langs: Language[]) => _.find(langs, { id: id }))
              .pluck('name');
}

J'ai utilisé la fonction _.find() de Lodash pour trouver l'enregistrement correspondant dans le tableau, mais si vous ne l'avez pas, vous pouvez toujours effectuer la recherche manuellement ou utiliser la fonction .filter(langs, ...)[0].

Si vous souhaitez effectuer la mise en cache et que vous savez que le nom d'une langue pour un identifiant donné ne changera jamais, vous pouvez utiliser la fonction _.memoize() de Lodash pour réutiliser la sortie pour une entrée donnée.

Je suis curieux de voir si cela fonctionne pour vous.

0
GregL

Pour une application de service clientèle que je supportais, j'avais besoin d'un moyen de masquer ou d'afficher des boutons, selon qu'un compte d'utilisateur avait un secteur d'activité particulier pas nécessairement utiliser les données telles quelles. J'appelle donc une méthode à partir du modèle et utilise map avec find puis subscribe.

      <button *ngIf="(lobs$ | async) ? getButtonVisibility('internet') : false" class="eing-account-header-drawers-button" (click)="openDrawer(drawers[drawers.internet],$event)">


public getButtonVisibility(lobName: string) {
  let isVisible;
  this.lobs$.map(x =>
    x.findIndex(y =>
      y.lobName.toLowerCase() == lobName.toLowerCase() && y.hasLob))
        .subscribe(x => { isVisible = x != -1});
  return isVisible;      
}
0
David Spenard