É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?
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 );
trouver un élément dans Observable <Type>
let item: Language;
langugages. // observable cached data
.subscribe((items: Language[]) => item = items.find(p => p.id == 3));
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>
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]); );
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.
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;
}