RXJS AngularFire - 从可观察对象中获取Observables

4

我一直在尝试使用Angular从Firebase获取数据。

程序概述

这是一个小应用程序,用于存储餐食菜谱及其配料,以生成基于膳食计划的累积购物清单-每日、每周等。

我拥有什么:

1. Firebase数据库结构(其中的片段)

    Ingredients (collection)
      Id: string
      Name: string
      Tags: array of string
    Recipes (collection)
      CreatedOn: Timestamp
      MealTitle: string
      MealType: string
      Method: string
      RecipeIngredients (subcollection)
        Id: string
        IngredientId: string
        ExtendedDescription: string
        QTY: number
        UOM: string

如您所见,食谱集合包含一个配方成分子集合,其包含来自成分集合的成分ID和一些其他数据。

2. 获取特定食谱的配方成分子集合中成分列表的方法

  getRecipeIngredients(recipeId: string): Observable<RecipeIngredient[]> {
    this.recipesCollection = this.afs.collection('Recipes', ref => ref.orderBy('CreatedOn', 'desc'));

    this.recipeIngredients = this.recipesCollection.doc(recipeId).collection('RecipeIngredients').snapshotChanges().pipe(
      map(changes => {
        return changes.map(action => {
          const data = action.payload.doc.data() as RecipeIngredient;
          data.Id = action.payload.doc.id;

          return data;
        })
      })
    )

    return this.recipeIngredients;
  }

3. 根据 Id 获取特定成分的方法

getIngredientById(ingredientId: string): Observable<Ingredient> {
    this.ingredientDoc = this.afs.doc<Ingredient>(`Ingredients/${ingredientId}`);

    this.ingredient = this.ingredientDoc.snapshotChanges().pipe(
      map(action => {
        if (action.payload.exists === false) {
          return null;
        } else {
          const data = action.payload.data() as Ingredient;
          data.Id = action.payload.id;
          return data;
        }
      })
    )

    return this.ingredient;
  }

4. 我要实现的目标

我想在一个表格中显示特定食谱的配料清单,包含以下列:配料名称、详细描述、数量、单位。问题是我无法找到如何获取配料名称。

对我来说,一个合理的解决方案是在 getRecipeIngredients 内部完成它并通过该字段或整个 Ingredients 集合扩展其返回结果。我尝试过 RxJS 运算符,如 mergeFlat 或 combineLatest,但卡住了。

当然我可以改变数据库结构,但这也是我的学习项目。我相信我迟早会遇到这种挑战。

1个回答

1
这是一个有趣的问题,但我认为我已经解决了。请查看我的StackBlitz。这个决策树通常足以为大多数Observable场景提供函数,但我相信这个需要组合使用多个函数。
相关代码:
const recipeId = 'recipe1';
this.recipe$ = this.getRecipeIngredients(recipeId).pipe(concatMap(recipe => {
  console.log('recipe: ', recipe);
  const ingredients$ = recipe.recipeIngredients.map(recipeIngredient => this.getIngredientById(recipeIngredient.id));
  return forkJoin(ingredients$)
    .pipe(map((fullIngredients: any[]) => {
      console.log('fullIngredients: ', fullIngredients);
      fullIngredients.forEach(fullIngredient => {
        recipe.recipeIngredients.forEach(recipeIngredient => {
          if (recipeIngredient.id === fullIngredient.id) {
            recipeIngredient.name = fullIngredient.name;
          }
        });
      });
      return recipe;
  }));
})).pipe(tap(finalResult => console.log('finalResult: ', finalResult)));

我在代码中加入了控制台日志,以帮助说明数据的状态在传输过程中。主要思路是使用concatMap告诉食谱Observable,一旦完成此Observable,需要使用另一个Observable进行映射,然后使用forkJoin获取每个食谱配料的完整表示,并最终将这些结果映射回原始食谱。

嗨,非常感谢你的帮助。看起来这就是我要寻找的东西。 - Marek Kotowski

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接