Проблема с использованием ngrx-router и ngrx-data

avatar
Ninii
1 июля 2021 в 20:03
369
1
2

В настоящее время я использую ngrx в своем приложении angular 12. Я пытался понять, как можно использовать вместе ngrx-router и ngrx-data.

У меня есть несколько файлов:

app.module.ts (интересные строки)

/**
 * Store Modules
 */
StoreModule.forRoot(reducers, { metaReducers }),
StoreDevtoolsModule.instrument({ maxAge: 25 }),
EffectsModule.forRoot([RouterEffects]),
StoreRouterConnectingModule.forRoot({ routerState: RouterStateMinimal }),
EntityDataModule.forRoot(entityConfig),

статьи-коллекции.service.ts

import { Injectable } from '@angular/core';
import {
  EntityCollectionServiceBase,
  EntityCollectionServiceElementsFactory,
} from '@ngrx/data';
import { Observable } from 'rxjs';
import { Article } from '../models/articles/article';
import * as fromArticle from '../store/article/article.selectors';

@Injectable({ providedIn: 'root' })
export class ArticleCollectionService extends EntityCollectionServiceBase<Article> {
  active$: Observable<Article>;

  constructor(elementsFactory: EntityCollectionServiceElementsFactory) {
    super('Article', elementsFactory);
    this.active$ = this.store.select(fromArticle.selectActive);
  }
}

index.ts

import { routerReducer, RouterReducerState } from '@ngrx/router-store';
import { ActionReducerMap, MetaReducer } from '@ngrx/store';
import { navigationReducer } from './navigation/navigation.reducer';
import { NavigationState } from './navigation/navigation.state';

export interface RootState {
  router: RouterReducerState;
  navigation: NavigationState;
}

export const reducers: ActionReducerMap<RootState> = {
  router: routerReducer,
  navigation: navigationReducer,
};

export const metaReducers: MetaReducer[] = [];

router.selectors.ts

    import { getSelectors } from '@ngrx/router-store';
    import { RootState } from '..';
    
    export const selectFeature = (state: RootState) => state.router;
    
    export const {
        selectCurrentRoute,
        selectFragment,
        selectQueryParams,
        selectQueryParam,
        selectRouteParams,
        selectRouteParam,
        selectRouteData,
        selectUrl,
    } = getSelectors(selectFeature);

article.selectors.ts

import { EntitySelectorsFactory } from '@ngrx/data';
import { createSelector } from '@ngrx/store';
import { Article } from '../../models/articles/article';
import * as fromRouter from '../router/router.selectors';

export const {
  selectEntities,
  selectEntityMap,
} = new EntitySelectorsFactory().create<Article>('Article');

export interface ArticleStats {
  total: number;
}

export const selectStats = createSelector(
  selectEntities,
  (articles): ArticleStats => {
    return {
      total: articles.length,
    };
  }
);

export const selectActiveId = fromRouter.selectRouteParam('id');

export const selectActive = createSelector(
  selectEntityMap,
  selectActiveId,
  (entities, id) => entities[id!]
);

В последний файл я добавил entities[id!], потому что selectRouteParam('id') дает мне неопределенный результат как возможный. Я думаю, это имеет смысл, так как идентификатор URL не всегда будет определен

эта строка this.active$ = this.store.select(fromArticle.selectActive); выдает следующую ошибку

No overload matches this call.
  Overload 1 of 9, '(mapFn: (state: EntityCache) => Article | undefined): Observable<Article | undefined>', gave the following error.
    Argument of type 'MemoizedSelector<RootState, Article | undefined, DefaultProjectorFn<Article | undefined>>' is not assignable to parameter of type '(state: EntityCache) => Article | undefined'.
      Types of parameters 'state' and 'state' are incompatible.
        Type 'EntityCache' is missing the following properties from type 'RootState': router, navigation
  Overload 2 of 9, '(key: string | number): Observable<EntityCollection<any>>', gave the following error.
    Argument of type 'MemoizedSelector<RootState, Article | undefined, DefaultProjectorFn<Article | undefined>>' is not assignable to parameter of type 'string | number'.
      Type 'MemoizedSelector<RootState, Article | undefined, DefaultProjectorFn<Article | undefined>>' is not assignable to type 'number'.

В моем article-collections.service.ts я получаю эту ошибку, которую я не понимаю. После просмотра кажется, что это связано с неправильными объявлениями интерфейса, но я не могу понять, где я сделал что-то не так.

Источник

Ответы (1)

avatar
jalex19
1 июля 2021 в 20:40
1

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

custom-url-serializer.ts

import { RouterStateSerializer } from '@ngrx/router-store';
import { Params, RouterStateSnapshot } from '@angular/router';

export interface CustomRouterStateUrl {
 url: string;
 params: Params;
 queryParams: Params;
}

export class CustomSerializerUrl implements RouterStateSerializer<CustomRouterStateUrl> {
  serialize(routerState: RouterStateSnapshot): CustomRouterStateUrl {
    let route = routerState.root;

    while (route.firstChild) {
      route = route.firstChild;
    }

    const { url, root: { queryParams }} = routerState;
    const { params } = route;

    return { url, params, queryParams };
  }
}

Вам необходимо использовать этот сериализатор в селекторе магазина маршрутизатора

import { CustomRouterStateUrl} from './custom-url-serializer';
import { RouterReducerState } from '@ngrx/router-store';
import { createFeatureSelector, createSelector } from '@ngrx/store';
export const getRouterState = createFeatureSelector<
  RouterReducerState<CustomRouterStateUrl>
>('router');

export const getCurrentRoute = createSelector(getRouterState, (router) => {
  return router.state;
});
Ninii
1 июля 2021 в 21:21
0

Спасибо, это позволило мне заставить его работать :)