Angular Firestore 如何展示文档中内嵌映射类型字段的内容
Angular Firestore遍历内嵌映射对象字段方案
问题描述
本问题前置说明较长,核心语法相关问题位于说明末尾,可滚动查看。
我使用的 Firestore 数据库中有包含映射类型字段的文档,示例如下:
除 ingredients 字段外,文档本身还有其他字段,以下是图中 Congo Split 食谱的 JSON 格式表示:
"congosplit": { "ingredients": { "a": { "ingredientAmounts": "2 Pump, 3 Pumps, 4 Pumps", "ingredientName": "Banana Syrup", "ingredientSizes": "Small, Medium, Large" }, "b": { "ingredientAmounts": "2 Pump, 3 Pumps, 4 Pumps", "ingredientName": "White Chocolate", "ingredientSizes": "Small, Medium, Large" }, "d": { "ingredientAmounts": "10 oz, 12 oz, 16 oz", "ingredientName": "Steamed Milk and Foam", "ingredientSizes": "Small, Medium, Large" } }, "instructions": { "a": { "text": "Free pour milk into cup. Top with 1/4 inch foam. Make a circle with chocolate drizzle and use a thermometer to spread the chocolate into a flower pattern.", "type": "text" } }, "name": "Congo Split", "temperature": "hot" }
从上述结构可以看出,ingredients 和 instructions 字段都可能包含多个元素,采用 a、b、c 这样的键来标识不同条目。
我参照 Angular Firestore 示例代码完成了以下开发:
在 recipes.service.ts 文件中,我编写了如下方法获取 Firestore 中实时更新的食谱数据:
getRecipesFirestore() { this.recipesCollection = this.afs.collection('mission-cafe'); this.recipesData = this.recipesCollection.valueChanges(); return this.recipesData; }
在 home.ts 文件中调用该方法获取 recipesData:
ngOnInit() { // 其他逻辑省略 this.recipesData = this.recipesService.getRecipesFirestore(); }
目前我可以通过模板中的 *ngFor 指令正常展示大部分顶层字段的内容:
<ion-card *ngFor="let item of recipesData | async"> <ion-title>{{item.name}}</ion-title> </ion-card>
该写法可以正常渲染所有文档顶层字段的内容,但当我尝试展示 ingredients 或 instructions 的子元素时,页面显示 [object] 而非实际内容。以下是我尝试的错误写法示例:
<ion-card *ngFor="let item of recipesData | async"> <ion-title>{{item.name}}</ion-title> <div *ngFor="let ingred of item"> <ion-text>{{ingred.ingredientName}}</ion-text </div> </ion-card>
核心问题:
我找不到正确的语法来访问食谱字段内嵌映射对象的内容。我知道可以手动解析 JSON(之前使用实时数据库时就是这么做的),但我希望能用上 Firestore 配套的简洁模板语法来实现该需求,请问正确的写法是什么?
解决方案
Angular 6及以上版本内置了keyvalue管道,可直接在模板中遍历对象类型的字段,无需手动做JSON解析,适配Firestore返回的映射结构,正确写法如下:
模板代码
<ion-card *ngFor="let item of recipesData | async"> <ion-title>{{item.name}}</ion-title> <!-- 遍历ingredients映射 --> <div *ngFor="let ingredEntry of item.ingredients | keyvalue"> <!-- ingredEntry.key对应a/b/d这类键,ingredEntry.value对应具体的配料对象 --> <ion-text>{{ingredEntry.value.ingredientName}}</ion-text> <p>可选规格:{{ingredEntry.value.ingredientSizes}}</p> <p>对应用量:{{ingredEntry.value.ingredientAmounts}}</p> </div> <!-- 遍历instructions映射同理 --> <div *ngFor="let instructionEntry of item.instructions | keyvalue"> <p>操作步骤:{{instructionEntry.value.text}}</p> </div> </ion-card>
可选:自定义排序规则
如果需要按键的顺序固定排序,可以给keyvalue管道传入自定义排序函数:
- 在home.ts中添加排序方法
import { KeyValue } from '@angular/common'; // 按字母升序排序键 sortByKey = (a: KeyValue<string, any>, b: KeyValue<string, any>): number => { return a.key.localeCompare(b.key); }
- 模板中调用排序方法
<div *ngFor="let ingredEntry of item.ingredients | keyvalue:sortByKey">
低版本Angular兼容方案
如果你的Angular版本低于6,没有内置keyvalue管道,可以自行封装简易的自定义管道:
- 新建map-values.pipe.ts
import { Pipe, PipeTransform } from '@angular/core'; @Pipe({ name: 'mapValues' }) export class MapValuesPipe implements PipeTransform { transform(value: Record<string, any>): any[] { return value ? Object.values(value) : []; } }
- 在对应模块的declarations中引入该管道后,模板中直接使用:
<div *ngFor="let ingred of item.ingredients | mapValues"> <ion-text>{{ingred.ingredientName}}</ion-text> </div>
内容的提问来源于stack exchange,提问作者Paul Mullen
相关产品推荐
相关产品推荐

