如何用Angular ngFor指令正确渲染嵌套对象数据?
问题
我有一个包含嵌套对象的productlists数组,想用Angular的ngFor指令在HTML中展示数据,但出现了重复渲染的问题。
数据结构
productlists = [ { productname: 'orange', price: [{ small: 20 }, { medium: 30 }, { large: 40 }], gst: 12 }, { productname: 'lemon', price: [{ small: 40 }, { medium: 80 }, { large: 102 }], gst: 20 }, { productname: 'apple', price: [{ small: 40 }, { medium: 80 }, { large: 102 }], gst: 20 } ];
现有HTML代码
<div style="margin-left: 20rem;"> <div *ngFor="let product of productlists" > <p style="color: blue;">Product Name {{product.productname}}</p> <div *ngFor="let productprice of product.price"> Product Price <p>Small: {{productprice.small}}</p> <p>Medium: {{productprice.medium}}</p> <p>Large: {{productprice.large}}</p> </div> <p>Product GST {{product.gst}}</p> </div> </div>
错误渲染效果
每个产品的价格部分会重复渲染三次,每次仅显示一个规格的价格(第一次显示Small,Medium和Large为空;第二次显示Medium,其余为空;第三次显示Large,其余为空),页面出现冗余的空内容和重复的“Product Price”文本。
解决方法
问题根源在于price字段是包含3个独立对象的数组,每个对象仅存储一个规格的价格。用ngFor循环该数组时,会遍历3次,每次只能拿到单个规格的数据,因此出现重复渲染。
方案1:修改数据结构(推荐)
将price改为单个对象,一次性包含所有规格:
productlists = [ { productname: 'orange', price: { small: 20, medium: 30, large: 40 }, gst: 12 }, { productname: 'lemon', price: { small: 40, medium: 80, large: 102 }, gst: 20 }, { productname: 'apple', price: { small: 40, medium: 80, large: 102 }, gst: 20 } ];
对应的HTML代码去掉内层ngFor,直接读取对象属性:
<div style="margin-left: 20rem;"> <div *ngFor="let product of productlists"> <p style="color: blue;">Product Name {{product.productname}}</p> <div> Product Price <p>Small: {{product.price.small}}</p> <p>Medium: {{product.price.medium}}</p> <p>Large: {{product.price.large}}</p> </div> <p>Product GST {{product.gst}}</p> </div> </div>
方案2:不修改数据结构,合并规格数据
如果无法调整原始数据,可以通过组件内处理或管道合并数据:
方式A:组件内预处理数据
在组件的ngOnInit中合并每个产品的price数组:
ngOnInit() { this.productlists = this.productlists.map(product => ({ ...product, price: product.price.reduce((acc, curr) => ({...acc, ...curr}), {}) })); }
处理后HTML代码同方案1。
方式B:模板中使用自定义管道
创建合并对象的管道:
import { Pipe, PipeTransform } from '@angular/core'; @Pipe({name: 'mergeObjects'}) export class MergeObjectsPipe implements PipeTransform { transform(arr: any[]): any { return arr.reduce((acc, curr) => ({...acc, ...curr}), {}); } }
在模块中声明该管道后,模板中使用:
<div style="margin-left: 20rem;"> <div *ngFor="let product of productlists"> <p style="color: blue;">Product Name {{product.productname}}</p> <div> Product Price <ng-container *ngIf="product.price | mergeObjects as mergedPrice"> <p>Small: {{mergedPrice.small}}</p> <p>Medium: {{mergedPrice.medium}}</p> <p>Large: {{mergedPrice.large}}</p> </ng-container> </div> <p>Product GST {{product.gst}}</p> </div> </div>
内容的提问来源于stack exchange,提问作者Reegan
相关产品推荐
相关产品推荐

