firebase - Angular 6、NGXS 和 Firestore

标签 firebase architecture google-cloud-firestore angular6 ngxs

我有一个 Firestore 数据库,其集合的排列方式与关系数据库类似,相关集合存储外键。做出此决定是为了更轻松地与现有 .Net 服务集成并提供更大的灵活性。

这是数据模型的一部分:

enter image description here

我已将状态拆分为功能模块并具有如下代码:

  ngxsOnInit(ctx: StateContext<SchedulesStateModel>) {
    ctx.dispatch([
      new GetScheduleGroups(),
      new GetSchedules()
    ]);
  }

  @Selector()
  static scheduleGroups(state: SchedulesStateModel): Array<ScheduleGroup> {
    return state.scheduleGroups;
  }

  @Selector()
  static scheduleGroupById(state: SchedulesStateModel) {
    return (scheduleGroupId: string) => {
      return state.scheduleGroups.filter((scheduleGroup: ScheduleGroup) =>
        scheduleGroup.id === scheduleGroupId);
    };
  }

  @Action(GetScheduleGroups)
  getScheduleGroups({ patchState }: StateContext<SchedulesStateModel>) {
    return this.dataService.getDocuments$<ScheduleGroup>('scheduleGroups')
      .pipe(tap(scheduleGroups => patchState({ scheduleGroups })));
  }

  @Action(GetSchedules)
  getSchedules({ patchState }: StateContext<SchedulesStateModel>) {
    return this.dataService.getDocuments$<Schedule>('schedules')
      .pipe(tap(schedules => patchState({ schedules })));
  }

在 ScheduleListComponent 中,我有以下代码:

  getSchedulesById$(scheduleGroupId: string) {
    return this.store.select(SchedulesState.schedulesByGroupId)
      .pipe(map(schedulesFilter => schedulesFilter(scheduleGroupId)));
  }

我可以将 getSchedulesById$ 的检索逻辑移至状态类中,但就我目前的代码结构而言,我需要一种为每个 ScheduleGroup 检索 Schedule 的方法;因为 ScheduleGroup 被迭代并且每个组的 Schedules 被检索以供显示。这可以在以下模板片段中看到:

<ngb-tab
  *ngFor="let scheduleGroup of (getScheduleGroups$(scheduleGroupCollection.id) | async)"
  [title]="scheduleGroup.name">
  <ng-template ngbTabContent>
    <div class="schedules-container" [ngStyle]="{ height: tabHeight }">
      <ibms-schedules-list-item
        *ngFor="let schedule of (getSchedules$(scheduleGroup.id) | async)"
        [schedule]="schedule">
      </ibms-schedules-list-item>
    </div>
  </ng-template>
</ngb-tab>

这段代码可以工作,但看起来很冗长。有更好的方法吗?

我遇到的问题之一是,即使是简单的组件最终也会变成容器。以ScheduleListComponent为例,它需要显示相关表中的数据。获取此数据取决于 *ngFor 迭代期间哪个特定的 Schedule 项目处于焦点。

有什么改进建议吗?我几乎肯定有更好的方法,但我不确定它是什么。

此外,这是数据服务:

import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { fromPromise } from 'rxjs/internal-compatibility';
import { map } from 'rxjs/operators';

import { AngularFirestore } from 'angularfire2/firestore';
import WhereFilterOp = firebase.firestore.WhereFilterOp;

import { Guid } from 'guid-typescript';

@Injectable({
  providedIn: 'root'
})
export class DataService {

  constructor(private db: AngularFirestore) {}

  getDocuments$<T>(collectionName: string): Observable<T[]> {
    return this.db.collection<T>(collectionName).snapshotChanges().pipe(
      map(actions => actions.map(action => {
        const id = action.payload.doc.id;
        const data: T = action.payload.doc.data() as T;
        data[ 'id' ] = id;
        return <T>data;
      }))
    );
  }

  getDocumentsByQuery$<T>(collectionName: string, propertyName: string,
                          comparisonOperator: WhereFilterOp, targetValue: string | boolean): Observable<T[]> {
    return this.db.collection<T>(
      collectionName,
      ref => ref.where(propertyName, comparisonOperator, targetValue)).snapshotChanges().pipe(
      map(actions => actions.map(action => {
        const id = action.payload.doc.id;
        const data: T = action.payload.doc.data() as T;
        data[ 'id' ] = id;
        return data;
      }))
    );
  }

  addDocument<T>(collectionName: string, document: T) {
    const guid: string = Guid.raw();
    return fromPromise(this.db.collection<T>(collectionName).doc(guid).set(document))
      .pipe(
        map(() => {
          const returnDocument: T = document;
          document[ 'id' ] = guid;
          return returnDocument;
        })
      );
  }

  addBatch<T>(collectionName: string, documents: Array<T>) {
    const batch = this.db.firestore.batch();
    const collectionRef = this.db.collection<T>(collectionName);

    documents.forEach((document: T) => {
      const guid: string = Guid.raw();
      const docRef = collectionRef.doc(guid).ref;
      batch.set(docRef, document);
    });

    return fromPromise(batch.commit());
  }

  updateDocument<T>(collectionName: string, document: T, data: any) {
    return this.db.collection<T>(collectionName).doc(document['id']).update(data);
  }

  deleteDocument<T>(collectionName: string, document: T) {
    return this.db.collection<T>(collectionName).doc(document['id']).delete();
  }

}

我知道这项服务可以改进,我会抽出时间来做这件事,但我想为了完整性而将其包括在内。

如有任何改进建议,我们将不胜感激。

最佳答案

一些可能值得考虑的建议:

由于计划始终在组内使用,因此使用 @Selector 来投影该结构可能会更容易/更整洁。

它仍然可以是带有 scheduleGroupId 参数的动态选择器,但不是仅返回组,而是可以返回带有 schedules 子集合的组。然后,在模板中,您只有一个订阅的 Observable,它会返回您想要的数据形状。该投影可能更适合 UI,而如果您稍后通过时间表上的 CRUD 操作修补该状态,那么保持扁平结构可能会让生活变得更轻松?

One of the problems that I have is that even simple components end up becoming containers. Using the ScheduleListComponent as an example, it needs to display data from a related table. Fetching this data depends on which particular Schedule item is in focus during an *ngFor iteration.

我不太清楚你的意思 - 因为你正在通过 NGXS 初始化 Hook 将操作分派(dispatch)到 GetSchedulesGetScheduleGroups 你将拥有无论如何,一切都急切地取来,对吗?我可能在这里误解了一些东西。

组件 SchedulesListItem 是否会导致问题?

关于firebase - Angular 6、NGXS 和 Firestore,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52846673/

相关文章:

java - firebase android 基于类的更新不尊重字段名称的大小写

oop - 一些可重用代码的架构

architecture - 使用 CQRS 进行多对多关系的替代方案

typescript - 如何使用 Typescript 从 firebase (8+) 正确导入 Timestamp 类

ios - 如何明智地将 FirebaseFirestoreSwift 与 Carthage 结合使用?

node.js - 在 Firestore 上的文档中设置 DocumentReference (NodeJS)

node.js - 如何将nodeJS项目托管到firebase?

c# - 为 UserControl 填充模型的正确方法是什么?

firebase - Firebase Cloud Firestore : fetching documents over collections 的成本优化模式

javascript - 在具有两个或多个返回 promise 的方法的 Firebase Cloud Function 中返回什么?