问题
Given the following Typescript in an Angular 2 service:
getLanguages () {
return this.http.get(this._languagesUrl)
.map(res => <Language[]> res.json().data)
.catch(this.handleError);
I'm having difficulty using this in circumstances where I need to lookup a specific item from the array. For example, I can't do the following because filter
expects an Observable<Language>
rather than an the Observable<Language[]>
that is being returned.
getLanguages().filter(language => language.id == 3) // Error
I appreciate that my issue may be that I'm mixing synchronous and asynchronous behavior, so Ill provide my use case: User can enter a language id and I want to display the associated language name. I want to leverage getLanguages()
with the Observable
result because it is already being used elsewhere in the project. I also want implement some caching so the HTTP request doesn't get made each time I do a lookup.
Any thoughts?
回答1:
Here's an example working of what you want to do:
https://plnkr.co/edit/lK47pVaW8b0CEez0mum4?p=preview
Press F12 in chrome to see the logs to give you a more clear idea of what's going on and why it doesn't work in your example.
Take a special atention to:
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 ) );
}
This is another, maybe better, way to do it:
_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:
finding item in Observable<Type>
let item: Language;
langugages. // observable cached data
.subscribe((items: Language[]) => item = items.find(p => p.id == 3));
回答3:
You can use mergeMap
instead of map
to flatten out the array:
getLanguages () {
return this.http.get(this._languagesUrl)
.flatMap(res => Rx.Observable.fromArray(<Language[]> res.json().data))
.catch(this.handleError);
}
Now the return value from getLanguages
will be an Observable<Language>
回答4:
If you follow the map
method with subscribe
, you can access members of the Language
array when the array is available:
return this.http.get(this._languagesUrl)
.map(res => <Language[]> res.json().data)
.subscribe((languages: Language[]) => alert(languages[3]); );
回答5:
This is something of a guess, since I haven't used RxJS personally, though I am interested in it. Perusing the docs, I wonder if the following would work:
function getLanguageName(id: number): Observable<string> {
return getLanguages()
.map((langs: Language[]) => _.find(langs, { id: id }))
.pluck('name');
}
I used Lodash's _.find()
to find the matching record in the array, but if you don't have it, you can always do the search manually or use .filter(langs, ...)[0]
.
If you want to do caching, and you know that the name of a language for a given id will never change, you could use Lodash's _.memoize()
function to reuse the output for a given input.
I'm curious to see if this works for you.
回答6:
For a customer service app I'm supporting, I needed a way to hide or show buttons depending on whether or not a user account had a particular line of business, and I needed to act upon the async data being loaded on the page, and not necessarily use the data as-is. So I'm calling a method from the template and using map with find then 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;
}
来源:https://stackoverflow.com/questions/35351922/angular-2-way-to-get-item-from-observablexyz