我有一个 Firestore 数据库,其集合的排列方式与关系数据库类似,相关集合存储外键。做出此决定是为了更轻松地与现有 .Net 服务集成并提供更大的灵活性。
这是数据模型的一部分:
我已将状态拆分为功能模块并具有如下代码:
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)到 GetSchedules
和 GetScheduleGroups
你将拥有无论如何,一切都急切地取来,对吗?我可能在这里误解了一些东西。
组件 SchedulesListItem
是否会导致问题?
关于firebase - Angular 6、NGXS 和 Firestore,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52846673/