Angular 从 Observable 获取项目的 2 种方法<Xyz[]>

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

在 Angular 2 服务中给出以下 Typescript:

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

在需要从数组中查找特定项目的情况下,我很难使用它。例如,我无法执行以下操作,因为 filter 期望返回 Observable<Language> 而不是 Observable<Language[]>

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

我明白我的问题可能是我混合了同步和异步行为,所以我将提供我的用例:用户可以输入一个语言 ID,我想显示相关的语言名称。我想利用 getLanguages()Observable 结果,因为它已在项目的其他地方使用。我还想实现一些缓存,这样每次查找时都不会发出 HTTP 请求。

有什么想法吗?

这是一个猜测,因为我个人没有使用过 RxJS,尽管我对它很感兴趣。仔细阅读文档,我想知道以下是否可行:

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

我使用 Lodash 的 _.find() 来查找数组中的匹配记录,但如果您没有,您可以随时手动进行搜索或使用 .filter(langs, ...)[0].

如果你想做缓存,并且你知道给定 id 的语言名称永远不会改变,你可以使用 Lodash 的 _.memoize() 函数为给定输入重用输出。

我很好奇这是否适合你。

如果在 map 方法之后使用 subscribe,则可以在数组可用时访问 Language 数组的成员:

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

下面是您想要执行的操作的示例:

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

在 chrome 中按 F12 查看日志,让您更清楚地了解发生了什么以及为什么它在您的示例中不起作用。

特别注意:

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

这是另一种也许更好的方法:

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

您可以使用 mergeMap 而不是 map 来展平数组:

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

}

现在 getLanguages 的 return 值将是 Observable<Language>

在 Observable 中查找项目

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

对于我支持的客户服务应用程序,我需要一种方法来根据用户帐户是否具有特定业务线来隐藏或显示按钮,并且我需要对正在加载的异步数据采取行动在页面上,不一定按原样使用数据。所以我从模板调用一个方法并使用 map 和 find 然后订阅。

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