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

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

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

Цель

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

То, что я достиг до сих пор:

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

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

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

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 Collection с Свойство sortQuery , как видно на этом снимке экрана: enter image description here and updates it also according to the changed sortQuery property. However, I would prefer using the already initialized sortQuery property in the Resource entityCache collection (yeah, I also tried adding it to the entityCache feature with StoreModule.forFeature('entityCache', reducers) ignore that):

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

b) Таким образом, другой подход заключался в использовании 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 .

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

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

1 Ответ

1 голос
/ 30 июля 2020

Вы подошли к этому так же, как и я изначально: пытаясь зарегистрировать настраиваемый редуктор для обработки свойств 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 saveUpdateOne(collection: EntityCollection<T>, action: EntityAction<Update<T>>): EntityCollection<T> {
        const superMethod = super.saveUpdateOne(collection, action);

        if (action.payload.data.changes.hasOwnProperty('lastProjectId')) {
            (superMethod as any).lastProjectId = (action.payload.data.changes as any).lastProjectId;
        }

        return superMethod;
    }
}

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

extended-entity-collection-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 необходимо заменить исходный.

app-module.ts

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

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

this.someService.update({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);
    }
}

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

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

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

...