как РАСШИРЯТЬ редукторы сбора пользовательских сущностей в @ ngrx / data

Прочитав и попробовав все до сих пор, чтобы обновить мои additionalCollectionStates до моей EntityCollection (на основе небольшой информации из документов, этот вопрос SO, этот другой вопрос SO, еще один вопрос SO и проблема из репозитория github) Я застрял в том, что должно быть довольно распространенной задачей, и не было никаких проблем до использования ngrx: сортировка сохраненной коллекции сущностей с помощью ngrx. Поиск решений возвращает меня к тем же 10 ссылкам, которые я уже видел 5 раз, и большинство примеров либо слишком просты, либо устарели, либо вообще не используют ngrx / data, либо на аналогичные вопросы вообще нет ответов.

Среда: Angular 9.1.7, ngrx 9.1.2, macOS 10.14.6.

Цель

Добавить и обновить собственный additionalCollectionState в мою коллекцию EntityCollection, как описано в документации для сортировки и разбивка на страницы. Я не получаю свойства sortQuery (sortField, sortDirection) с сервера, а устанавливаю их в своем графическом интерфейсе и сортирую коллекцию на их основе. Я знаю, что в EntityMetadataMap существует параметр sortComparer, но он недостаточно гибкий для моих средств, я хочу динамически сортировать коллекции в разных таблицах MatTables в приложении. Это дополнительное свойство следует обновлять всякий раз, когда изменяется порядок / поле сортировки в MatTable.

Чего я уже достиг:

Способ ngrx / data для additionalCollectionState, как описано выше, работает, моя коллекция инициализируется данными, которые я предоставил в EntityMetadataMap. Однако обновление свойств настраиваемой коллекции не работает так, как я понял из документации. Поскольку я не получаю свойство sortQuery с сервера, ResultsHandler не вызывается.

Я подошел к этой проблеме двумя способами: а) используя методы ngrx и ngrx / entity и б) способ ngrx / data, пытаясь расширить пользовательский EntityCollectionReducer

а) Мне удалось создать настраиваемое действие и настраиваемый редуктор и обновить свойство состояния, но не ранее определенное свойство коллекции. Мои действия в основном таковы:

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

Редюсеры экспортируются в redurs / 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
  }
}),

Это создает коллекцию resourceSort со свойством sortQuery, как видно на этом снимке экрана: введите описание изображения здесь и обновляет его также в соответствии с измененными sortQuery свойство. Однако я бы предпочел использовать уже инициализированное свойство sortQuery в коллекции Resource entityCache (да, я также пытался добавить его в функцию entityCache, StoreModule.forFeature('entityCache', reducers) игнорируя это):

введите описание изображения здесь

б) Таким образом, другой подход заключался в использовании EntityCollectionReducerRegistry в app.module:

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 - это главный редуктор для всех коллекций сущностей в кэше сохраненных сущностей. В библиотеке нет именованного типа редуктора сущностей. Скорее, он полагается на метод EntityCacheReducerFactory.create () для создания этого редуктора, которым является NgRx ActionReducer ‹EntityCache, EntityAction›.

У меня есть ощущение, что с подходом б) я могу быть на правильном пути, однако я застрял из-за отсутствия документации и примеров того, как справиться с этой проблемой - может ли кто-нибудь мне помочь?

Если требуются дополнительные сведения, я с радостью их предоставлю, а если что-то неясно, поскольку английский не является моим родным языком, я с удовольствием поясню, и, если необходимо, попытаюсь предоставить упрощенный пример stackblitz. Спасибо!


person hreimer    schedule 10.07.2020    source источник


Ответы (1)


Вы подошли к этому так же, как я изначально: пытаясь зарегистрировать настраиваемый редуктор для обработки свойств additionalCollectionState. Потратив некоторое время на расшифровку документации, я наконец смог обновить свойство коллекции. Вот что написано в документации и как я это сделал:

Библиотека данных NgRx генерирует селекторы для этих свойств, но не имеет возможности их обновить. Для этого вам придется создать или расширить существующие редукторы. (additionalCollectionState)

После этого мы переходим к примеру с серверной частью, но мне это не нужно; тем не менее, я использовал информацию с шага 2 и далее, чтобы расширить (для чтения, взломать) EntityCollectionReducerMethods, чтобы перехватить мое свойство:

extended-entity-collection-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;
}

Затем это должно быть создано Factory:

расширенная-коллекция-редуктор-методы-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 необходимо заменить исходный.

app-module.ts

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

Теперь у вас есть хитрый способ сделать это в вашем компоненте:

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

someService необходимо расширить чудесный класс EntityCollectionServiceBase:

some-service.ts

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

Метод updateOne в ExtendedEntityCollectionReducerMethods выбирает свойство через полезную нагрузку действия, и вы можете установить его там, прежде чем возвращать результат superMethod.

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

Я установил идентификатор для метода обновления равным 0, потому что в противном случае он будет жаловаться, что идентификатор отсутствует. Поскольку у меня нет сущности с идентификатором 0, она должна работать без каких-либо непредвиденных последствий.

person RepentAndBelieveTheGospel    schedule 29.07.2020
comment
Спасибо, чувак, ты делаешь работу лордов! Спустя недели, даже месяцы отсутствия решения, это просто работает - и пока не будет лучшего / официального метода или, по крайней мере, документации или передовой практики, я буду использовать этот подход. - person hreimer; 16.10.2020
comment
@hreimer Рад, что смог помочь. Вся слава принадлежит Господу и Спасителю Иисусу Христу. Я бы не был программистом, если бы не он. - person RepentAndBelieveTheGospel; 19.10.2020
comment
@RepentAndBelieveTheGospel привет человек. Спасибо за отличный пост ... будет ли это работать, если мне нужно сделать несколько инъекций в модуле? Как я могу подойти к этому? - person Morfinismo; 01.06.2021
comment
@Morfinismo Что вы имеете в виду под множественными инъекциями? Чего вы пытаетесь достичь? - person RepentAndBelieveTheGospel; 03.06.2021
comment
@RepentAndBelieveTheGospel Я имел в виду это ... stackoverflow.com/questions/67796773/ - person Morfinismo; 03.06.2021
comment
@Morfinismo Давайте продолжим обсуждение на этом. - person RepentAndBelieveTheGospel; 07.06.2021