RX JS AngularFire - получить наблюдаемые из Observable - PullRequest
4 голосов
/ 07 января 2020

Я пытался получить данные из Firebase (используя Angular).

Краткое содержание программы

Это небольшое приложение для хранения рецептов блюд и их ингредиентов. создать накопленный список покупок на основе планирования питания - ежедневно, еженедельно и т. д. c.

Что у меня есть:

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

Как вы можете видеть Рецепты коллекция содержит RecipeIngredients подколлекция, которая имеет IngredientId из Ингредиенты сбор и некоторые дополнительные данные.

2. Метод получения списка ингредиентов из подгруппы RecipeIngredients для спецификаций c recipe

  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. Способ получения указанного c ингредиента на основе его 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. Чего я хочу достичь

Я хочу отобразить список ингредиентов для указанного рецепта c в таблице, содержащей столбцы: Имя ингредиента, Расширенное описание, QTY, UOM. Дело в том, что я не могу понять, как получить имя ингредиента. Разумное решение для меня - сделать это внутри getRecipeIngredients и увеличить возвращаемый результат на это поле или на всю коллекцию Ingredients . Я пытался использовать операторы Rx JS, такие как mergeFlat или combLatest, но я застрял.

Конечно, я мог бы изменить структуру базы данных, но для меня это тоже учебный проект. Я верю, что рано или поздно у меня возникнет такая проблема.

1 Ответ

1 голос
/ 08 января 2020

Это было интересно, но я думаю, у меня все получилось. Пожалуйста, проверьте мой StackBlitz . Это дерево решений обычно достаточно хорошо, чтобы предоставить вам функцию для большинства наблюдаемых сценариев ios, но я считаю, что для этого требуется комбинация функций.

Соответствующий код:

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, что мне нужно сопоставить его с другой наблюдаемой, как только эта будет завершена, затем использовать forkJoin, чтобы получить полное представление каждого ингредиента рецепта, и, наконец, отобразить эти результаты возвращаются к оригинальному рецепту.

...