2

在阅读并尝试了到目前为止的所有内容以将我的 AdditionalCollectionStates 更新为我的 EntityCollection 之后(基于文档中的一些信息,this SO Questionthis other SO Questionanother SO question来自github存储库的问题)我发现自己陷入了困境应该是一个相当普遍的任务,在使用ngrx之前完全没有问题:使用ngrx对存储的实体集合进行排序。搜索解决方案将我带回到我已经看过 5 次的相同的 10 个链接,大多数示例要么太基本,要么过时,要么根本不使用 ngrx/data,或者类似的问题根本没有答案。

环境:Angular 9.1.7,ngrx 9.1.2,macOS 10.14.6

目标

如排序和分页文档中所述,将own 添加和更新additionalCollectionState到我的 EntityCollection 中。我没有从服务器获取sortQuery属性(sortField、sortDirection),而是在我的 GUI 中设置它并根据它们对集合进行排序。我知道 EntityMetadataMap 上存在设置,但它对我的方式不够灵活,我想在整个应用程序中对不同 MatTables 中的集合进行动态排序。每当 MatTable 中的排序顺序/字段发生更改时,都应更新此附加属性。sortComparer

到目前为止我所取得的成就:

如上所述的附加CollectionState 的ngrx/data 方式有效,我的集合使用我在EntityMetadataMap 中提供的数据进行初始化。但是,更新自定义集合属性并不能按照我从文档中理解的方式进行。由于我没有从服务器获取sortQuery属性,因此不会调用 ResultsHandler。

我通过两种方式解决了这个问题:a)使用 ngrx 和 ngrx/entity 方法和 b)通过尝试扩展自定义EntityCollectionReducer的 ngrx/data 方式

a) 我成功创建了自定义 Action 和自定义 Reducer 并更新了state 属性,但没有更新之前定义的集合属性。我的行动基本上是:

export const selectResourceSortQuery = createAction('[Resource] Get Last Sort Query', props<{ sortQuery: any }>());

它是从服务中调用的:

    this.store.dispatch(selectResourceSortQuery({ sortQuery }));

并在减速器中处理:

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);
}

reducer 在 reducers/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 ? [] : [];

在 app.module 中设置为:

StoreModule.forRoot(reducers, {
  metaReducers: [
    ngrxEntityRelationshipReducer //ignore this
  ],
  runtimeChecks: {
    strictStateImmutability: true,
    strictActionImmutability: true
  }
}),

这将创建一个带有sortQuery属性的resourceSort集合,如该屏幕截图所示:并根据更改后的 sortQuery 属性对其进行更新。但是,我更喜欢在 Resource entityCache 集合中使用已经初始化的 sortQuery 属性(是的,我也尝试将其添加到entityCache功能中并忽略它):在此处输入图像描述 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'))

从文档:

Entity Reducer 是存储实体缓存中所有实体集合的主 reducer。该库没有命名实体减速器类型。相反,它依赖于 EntityCacheReducerFactory.create() 方法来生成该 reducer,它是一个 NgRx ActionReducer<EntityCache, EntityAction>。

我有一种感觉,使用 b) 方法我可能走在正确的轨道上,但是我被缺乏如何处理这个问题的文档和示例所困扰 - 有没有人可以帮助我?

如果需要更多详细信息,我很乐意提供,如果有不清楚的地方,因为英语不是我的母语,我很乐意澄清,如有必要,请尝试提供一个简化的 stackblitz 示例。谢谢!

4

1 回答 1

4

您已经以与我最初相同的方式处理此问题:尝试注册自定义减速器来处理 additionalCollectionState 属性。在花了一些时间破译文档之后,我终于能够更新一个集合属性。这是文档所说的以及我是如何做到的:

NgRx 数据库为这些属性生成选择器,但无法更新它们。您必须自己创建或扩展现有的 reducer。(附加集合状态

在此之后,它继续使用后端示例,但我不需要它;然而,我使用从第 2 步开始的信息来扩展(要被破解)EntityCollectionReducerMethods 来拦截我的属性:

扩展实体集合缩减器方法.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;
}

然后这需要由工厂实例化:

扩展实体集合-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;
    }
}

ExtendedEntityCollectionReducerMethodsFactory 需要替换原来的那个。

应用模块.ts

providers: [
        {
            provide: EntityCollectionReducerMethodsFactory,
            useClass: ExtendedEntityCollectionReducerMethodsFactory
        }
    ]

现在,您可以在组件中偷偷摸摸地执行此操作:

this.someService.updateOneInCache({id: 0, lastProjectId: projectId} as any);

someService 需要扩展奇妙的 EntityCollectionServiceBase 类:

一些服务.ts

@Injectable({providedIn: 'root'})
export class SomeService extends EntityCollectionServiceBase<SomeEntity> {
    constructor(serviceElementsFactory: EntityCollectionServiceElementsFactory) {
        super('SomeEntity', serviceElementsFactory);
    }
}

ExtendedEntityCollectionReducerMethods 中的 updateOne 方法将通过操作的有效负载获取属性,您可以在返回 superMethod 的结果之前将其设置在那里。

我觉得这种感觉很糟糕,但它确实有效。在徒劳地尝试使用 EntityCacheReducerFactory 及其奇怪的伙伴之后,我发现没有其他方法可以更新集合的附加属性。

我已将更新方法的 id 设置为 0,否则它会抱怨 id 丢失。由于我没有任何 id 为 0 的实体,因此它应该可以正常工作而不会产生任何意外后果。

于 2020-07-29T21:22:07.810 回答