模板中 Observable collection 的 Observable

Observable of Observable collection in template

由于标题肯定不会进一步推动我的意图,这就是我正在尝试做的事情。

我想在视图中显示项目列表,这是我从后端异步获取的(我有一个服务,该服务使用名为 fetchItems() 的方法来获取它们,该方法具有 Observable<Item[]> return类型)。
我通过设置 this.items = this.itemService.fetchItems();.
在组件构造函数中初始化此列表 我通过 *ngFor="let item of items | async".

在视图中显示此项目列表

到目前为止,还不错,一切都按预期进行。

每个项目都有一个字段 subItemId。我想用这个键来显示,在前面提到的列表中,类型 SubItem 的实际子项 object。我可以通过 this.subItemService.findById(subItemId) 得到这个项目,其中 returns Observable<SubItem>.

我该怎么做?

我显然不能从模板中调用像 getSubItem(item: Item): SubItem 这样的组件方法,因为它会被一遍又一遍地调用。
我不想修改 itemService.fetchItem() 方法来急切地加载子项,因为它们可能在所有使用它的地方都不需要。

答案可能很明显,但我只使用 Angular (6) 几天。

您在模板中使用了 async,这意味着模板的数据绑定是延迟加载的。所以除了对子项目再次使用 async 之外,你真的无能为力。

<div *ngFor="let item of items | async">
    <span>{{(getSubItem(item: Item) | async)?.title}}</span>
</div>

上面会调用一个 returns 可观察的函数,? 意味着在读取 title 属性 之前结果是可选的。

你可以看到这里的限制是什么。如果您需要多个 属性,则必须执行另一个 async 操作。就是不实用。

您需要加载模板的所有数据或更改 API 后端以在单个 API 调用中获取所有数据。

你最终会需要这样的东西。

 this.itemService.fetchItems().first().subscribe((items)=>{ 
       this.items = items;
       this.items.forEach((item)=>{ 
            this.getSubItem(item).first().subscribe((sub)=> {
                 // attach the child to the parent item
                 item.child = sub;
            });
       });
 });

然后您可以在模板中遍历 items,并将 item.child 用于该父项的子项。您需要使用 ? 运算符,因为数据是延迟加载的,并且在第一次呈现项目时不存在。

您可以使用 rxjs 运算符重建 item > subitem 的父子关系。这是一个 example:

export class AppComponent {
  name = 'Angular 6';
  itemsWithSubItems: Observable<any>;
  subItems: Observable<any[]>;
  getItems(): Observable<{ subItemId: number, itemName: string, subItem?: any }[]> {
    return of([{ subItemId: 1, itemName: 'One'}, { subItemId: 2, itemName: 'Two' }, { subItemId: 3, itemName: 'Three' }, { subItemId: 4, itemName: 'Four' }])
      .pipe(shareReplay());
  }

  constructor() {
    this.itemsWithSubItems = this.getItems().pipe(
        flatMap(items => from(items)),
        switchMap(item => this.getSubItemById(item.subItemId), 
          (outerValue, innerValue, outerIndex, innerIndex) => {
            outerValue.subItem = innerValue;
            return outerValue
          }),
        toArray());
    this.itemsWithSubItems.subscribe(x => console.log('x', x))
  }

  getSubItemById(subItemId: number): Observable<{name: string}> {
    return of({ name: `${subItemId}-SubItem` });
  }
}