Angular 2 way to get item from Observable<Xyz[]>

夙愿已清 提交于 2019-12-21 09:33:24

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!