haj*_*ajn 3 angular ngrx-entity angular-ngrx-data ngrx-data
在阅读并尝试了迄今为止的所有内容以将我的additionalCollectionStates更新到我的EntityCollection(基于文档中的一些信息,这个SO问题,另一个SO问题,另一个SO问题和github存储库中的一个问题)之后,我发现自己陷入了困境应该是一个相当常见的任务,并且在使用 ngrx 之前根本没有问题:使用 ngrx 对存储的实体集合进行排序。搜索解决方案让我回到我已经看过 5 次的 10 个链接,大多数示例要么太基础,要么过时,要么根本不使用 ngrx/data,或者类似的问题根本没有答案。
环境:Angular 9.1.7、ngrx 9.1.2、macOS 10.14.6
目标
按照排序和分页文档中的描述,将自己添加并更新additionalCollectionState到我的 EntityCollection 。我没有从服务器获取sortQuery属性(sortField、sortDirection),而是在 GUI 中设置它并根据它们对集合进行排序。我知道 EntityMetadataMap 上存在设置,但它对我来说不够灵活,我想动态地对应用程序中不同 MatTable 中的集合进行排序。只要 MatTable 中的排序顺序/字段发生更改,就应该更新此附加属性。sortComparer
到目前为止我所取得的成就:
如上所述,additionalCollectionState 的 ngrx/data 方式有效,我的集合是用我在 EntityMetadataMap 中提供的数据初始化的。然而,更新自定义集合属性并不像我从文档中理解的那样工作。由于我没有从服务器获取sortQuery属性,因此不会调用 ResultsHandler。
我通过两种方式解决了这个问题:a)使用 ngrx 和 ngrx/entity 方法,b)通过尝试扩展自定义EntityCollectionReducer的 ngrx/data 方式
a) 我成功创建了自定义 Action 和自定义Reducer 并更新了状态属性,但没有更新之前定义的集合属性。我的行动基本上是:
export const selectResourceSortQuery = createAction('[Resource] Get Last Sort Query', props<{ sortQuery: any }>());
Run Code Online (Sandbox Code Playgroud)
它是从具有以下功能的服务中调用的:
this.store.dispatch(selectResourceSortQuery({ sortQuery }));
Run Code Online (Sandbox Code Playgroud)
并在减速器中处理:
import { EntityState, EntityAdapter, createEntityAdapter } from '@ngrx/entity';
import { Action, createReducer, on } from '@ngrx/store';
import * as ResourceActions from './resource.actions';
import { Resource } from 'src/app/sdk';
export interface State extends EntityState<Resource> {
// additional state property
sortQuery: any | null;
}
export const adapter: EntityAdapter<Resource> = createEntityAdapter<Resource>();
export const initialState: State = adapter.getInitialState({
// additional entity state properties
sortQuery: {
sortDirection: 'asc',
sortField: 'product'
},
});
export const resourceReducer = createReducer(
initialState,
on(ResourceActions.selectResourceSortQuery, (state, { sortQuery }) => {
console.log(`REDUCER called`);
return { ...state, sortQuery };
}),
);
export function reducer(state: State | undefined, action: Action) {
return resourceReducer(state, action);
}
Run Code Online (Sandbox Code Playgroud)
减速器在减速器/index.ts 中导出:
import {
ActionReducer,
ActionReducerMap,
createFeatureSelector,
createSelector,
MetaReducer
} from '@ngrx/store';
import { environment } from 'src/environments/environment';
import * as fromResourceReducers from 'src/app/store/entities/resource/resource.reducer';
export interface State {
}
export const reducers: ActionReducerMap<State> = {
resourceSort: fromResourceReducers.reducer
};
export const metaReducers: MetaReducer<State>[] = !environment.production ? [] : [];
Run Code Online (Sandbox Code Playgroud)
在 app.module 中设置为:
StoreModule.forRoot(reducers, {
metaReducers: [
ngrxEntityRelationshipReducer //ignore this
],
runtimeChecks: {
strictStateImmutability: true,
strictActionImmutability: true
}
}),
Run Code Online (Sandbox Code Playgroud)
这将创建一个具有sortQuery属性的资源排序集合,如以下屏幕截图所示:
并根据更改的 sortQuery 属性更新它。但是,我更喜欢使用资源实体缓存集合中已初始化的 sortQuery 属性(是的,我也尝试将其添加到实体缓存功能中并 StoreModule.forFeature('entityCache', reducers)忽略它):
b) 因此,另一种方法是在 app.module 中使用 EntityCollectionReducerRegistry:
import { resourceReducer } from './store/entities/resource/resource.reducer';
// does not work: resourceReducer is of type ActionReducer<State, Action> and the same as posted above (with the added handling of the sortQuery property), and apparently what this function expects
entityCollectionReducerRegistry.registerReducer('Resources', resourceReducer);
// this does work, but just creates the basic default entity collection reducer without my custom sortQuery handling
entityCollectionReducerRegistry.registerReducer('Resources', entityCollectionReducerFactory.create('resources'))
Run Code Online (Sandbox Code Playgroud)
来自文档:
EntityReducer 是存储实体缓存中所有实体集合的主减速器。该库没有命名实体减速器类型。相反,它依赖 EntityCacheReducerFactory.create() 方法来生成该减速器,即 NgRx ActionReducer<EntityCache, EntityAction>。
我有一种感觉,使用 b) 方法我可能走在正确的轨道上,但是我因缺乏如何处理这个问题的文档和示例而陷入困境 - 有人能够帮助我吗?
如果需要更多详细信息,我很乐意提供,如果由于英语不是我的母语而有不清楚的地方,我很乐意澄清,如果有必要,请尝试提供一个简化的 stackblitz 示例。谢谢!
您的处理方式与我最初的处理方式相同:尝试注册一个自定义化简器来处理additionalCollectionState 属性。在花了一些时间解读文档后,我终于能够更新集合属性。这是文档所说的以及我是如何做到的:
NgRx 数据库为这些属性生成选择器,但无法更新它们。您必须创建或扩展现有的减速器才能自己完成此操作。(附加集合状态)
之后,它继续后端示例,但我不需要那个;然而,我使用了步骤 2 以后的信息来扩展(将被解读为 hack)EntityCollectionReducerMethods 来拦截我的属性:
扩展实体集合reducer-methods.ts
export class ExtendedEntityCollectionReducerMethods<T> extends EntityCollectionReducerMethods<T> {
constructor(
public entityName: string,
public definition: EntityDefinition<T>) {
super(entityName, definition);
}
protected updateOne(collection: EntityCollection<T>, action: EntityAction<Update<T>>): EntityCollection<T> {
const superMethod = {...super.updateOne(collection, action)};
if (action.payload.entityName === 'Project' &&
(action.payload.data.changes as any).id === 0) {
(superMethod as any).lastProjectId = (action.payload.data.changes as any).lastProjectId;
}
return superMethod;
}
Run Code Online (Sandbox Code Playgroud)
然后需要由工厂实例化:
扩展实体集合reducer-methods-factory.ts
@Injectable()
export class ExtendedEntityCollectionReducerMethodsFactory {
constructor(
private entityDefinitionService: EntityDefinitionService
) {
}
create<T>(entityName: string): EntityCollectionReducerMethodMap<T> {
const definition = this.entityDefinitionService.getDefinition<T>(entityName);
const methodsClass = new ExtendedEntityCollectionReducerMethods(entityName, definition);
return methodsClass.methods;
}
}
Run Code Online (Sandbox Code Playgroud)
ExtendedEntityCollectionReducerMethodsFactory 需要替换原来的。
应用程序模块.ts
providers: [
{
provide: EntityCollectionReducerMethodsFactory,
useClass: ExtendedEntityCollectionReducerMethodsFactory
}
]
Run Code Online (Sandbox Code Playgroud)
现在您可以在组件中使用一种偷偷摸摸的方法来执行此操作:
this.someService.updateOneInCache({id: 0, lastProjectId: projectId} as any);
Run Code Online (Sandbox Code Playgroud)
someService 需要扩展出色的 EntityCollectionServiceBase 类:
某些服务.ts
@Injectable({providedIn: 'root'})
export class SomeService extends EntityCollectionServiceBase<SomeEntity> {
constructor(serviceElementsFactory: EntityCollectionServiceElementsFactory) {
super('SomeEntity', serviceElementsFactory);
}
}
Run Code Online (Sandbox Code Playgroud)
ExtendedEntityCollectionReducerMethods 中的 updateOne 方法将通过操作的有效负载选取属性,您可以在返回 superMethod 的结果之前将其设置在那里。
我觉得这种做法有点难堪,但它确实有效。在徒劳地尝试使用 EntityCacheReducerFactory 及其奇怪的家伙之后,我发现没有其他方法可以更新集合的附加属性。
我已将更新方法的 id 设置为 0,否则它会抱怨 id 丢失。由于我没有任何 id 为 0 的实体,因此它应该可以正常工作,不会产生任何意外后果。
| 归档时间: |
|
| 查看次数: |
3205 次 |
| 最近记录: |