如何使用AsyncPipe
内部 ngFor
,或者什么是对列表项进行异步调用的最佳实践?
示例: 假设我有一个产品订单,并且想显示产品的制造商。但是需要从存储(本地存储)中加载制造商才能显示出来。我最初的解决方案是使用以下(未经调试的伪代码):
HTML:
<ol>
<li *ngFor="let prod of order.toProducts">
{{ prod.Name }}: {{ DisplayManufacturer(prod) | async }}
</li>
<ol>
TypeScript:
public async DisplayManufacturer(prod: product) {
let manufacturer = await this.storageService.Load(prod.manufacturerId);
if (manufacturer) {
return manufacturer.name + ' (' + manufacturer.address + ')';
}
return "";
}
这-当然-导致无限循环。
我的下一个解决方案是遍历ngOnInit
内的所有订单及其所有产品,以找到制造商并将其存储在Map
中,以供以后在表达式中使用。
我认为应该有一个更好的解决方案-因为这还没有结束:我还必须实现DoCheck
来检查是否有任何产品阵列或制造商更改并重新加载了上述地图。而对于每个相似引用的字段,都是如此。
答案 0 :(得分:1)
在替换数据之前,您不需要使用异步/等待或加载所有数据。将字段添加到product
类型,并分配一个Promise或Observable来解析为所需数据。
服务
Load(id: any): any {
return new Promise(resolve => {
setTimeout(() => {
resolve(`Manufacturer id [${id}], name, address`);
}, Math.random() * 5000 + 2000);
});
}
组件
ngOnInit() {
for (let i = 1; i < 10; i++) {
this.order.toProducts.push(
{ Name: 'ProdName' + i, manufacturerId: i, data: this.storageService.Load('manufacturerId#' + i) },
);
}
}
模板
<ol>
<li *ngFor="let prod of order.toProducts">
{{ prod.Name }}: {{ prod.data | async }}
</li>
<ol>
答案 1 :(得分:0)
您可以获得产品:
products$ = this.myService.getProducts();
然后是制造商,并将他们加入产品:
manufacturersAndProducts$ = this.products$.pipe(
switchMap(products => forkJoin(products
.map(product => this.myService.getManufacturerForProduct(product))
)).pipe(
map(manufacturers => products
.map((product, index) => ({ product, manufacturer: manufacturers[index] }))
)
)
);
然后将其用于HTML
<ng-container *ngIf="manufacturersAndProducts$ | async as list">
<div *ngFor="let item of list">
{{ item.product.name }} is made by {{ item.manufacturer.name }}
</div>
</ng-container>
但这是一个不好的例子,因为使用HTTP调用,您实际上并不需要这种反应式编程。例如,当产品列表每10秒左右更改一次时,此功能很有用。
要回答您的疑问,您的问题是与上下文无关的:它不应该与异步有关,而应与模板方面的良好实践有关。
您不应在模板中使用不纯函数(具有副作用的函数),否则,正如您所说的,视图将再次呈现,这将导致无限循环或充其量出现性能问题。
您应该使用一个纯函数,或者构造一个包含所有数据的完整对象(就像我在这里所做的那样,但是您不需要异步管道)