问题
I have a state and I'd like to create selectors from ngrx/data entities.
import {
Action,
ActionReducer,
ActionReducerMap,
createFeatureSelector,
createSelector,
MetaReducer
} from '@ngrx/store';
import {environment} from '../../environments/environment';
import * as fromRouter from '@ngrx/router-store';
import * as fromDrawer from './drawer';
import {InjectionToken} from '@angular/core';
import {NavigationItem} from '../models/navigation-item';
export interface State {
router: fromRouter.RouterReducerState<any>;
drawerNavigationItems: fromDrawer.State;
}
export const ROOT_REDUCERS = new InjectionToken<ActionReducerMap<State, Action>>('Root reducers token', {factory: () => ({
router: fromRouter.routerReducer,
drawerNavigationItems: fromDrawer.reducer,
}),
});
export const metaReducers: MetaReducer<State>[] = !environment.production ? [] : [];
export const selectRouter = createFeatureSelector<
State,
fromRouter.RouterReducerState<any>
>('router');
const {
selectQueryParams, // select the current route query params
selectQueryParam, // factory function to select a query param
selectRouteParams, // select the current route params
selectRouteParam, // factory function to select a route param
selectRouteData, // select the current route data
selectUrl, // select the current url
} = fromRouter.getSelectors(selectRouter);
export const selectRouteId = selectRouteParam('id');
export const selectStatus = selectQueryParam('status');
// Drawer
export const selectDrawerNavigationItems = (state: State) => state.drawerNavigationItems.items as NavigationItem[];
How do I use the pre-defined selectors or write my own with entities or services that came from ngrx/data?
As an example I'd like to create a selector that selects all "Community" entities and then, step 2, select 1 by selectRouteId
.
If you imagine a route /communities/:id
, selectRouteId
returns the Id, and now I'd like the data from the CommunityService
and use the selector created or somehow imported and used in step 1 and return a result, 1 Community
with the selectRouteId
's id, so I can later do something like this.store.dispatch(selectCommunityByCurrentRouteId);
This question is specific to @ngrx/data.
回答1:
See https://github.com/peterbsmith2/platform/blob/b2f17bfcc987bf63d10dd207263c0ca2a2e44373/projects/ngrx.io/content/guide/data/extension-points.md#custom-selectors.
/* src/app/reducers/index.ts */
import * as fromCat from './cat.reducer';
import { Owner } from '~/app/models'
export const ownerSelectors = new EntitySelectorsFactory().create<Owner>('Owner');
export interface State {
cat: fromCat.State;
}
export const reducers: ActionReducerMap<State> = {
cat: fromCat.reducer
};
export const selectCatState = (state: State) => state.cat;
export const {
selectAll: selectAllCats
} = fromCat.adapter.getSelectors(selectCatState);
export const selectedCatsWithOwners = createSelector(
selectAllCats,
ownerSelectors.selectEntities,
(cats, ownerEntities) => cats.map(c => ({
...c,
owner: ownerEntities[c.owner]
}))
);
回答2:
a supplementary answer, to concretely answer my own question, here's what the reducers/index.ts looks like now
import {
Action,
ActionReducer,
ActionReducerMap,
createFeatureSelector,
createSelector,
MetaReducer
} from '@ngrx/store';
import {environment} from '../../environments/environment';
import * as fromRouter from '@ngrx/router-store';
import * as fromDrawer from './drawer';
import {InjectionToken} from '@angular/core';
import {NavigationItem} from '../models/navigation-item';
import {EntitySelectorsFactory} from '@ngrx/data';
import {Community} from '../models/community';
export interface State {
router: fromRouter.RouterReducerState<any>;
drawerNavigationItems: fromDrawer.State;
}
export const ROOT_REDUCERS = new InjectionToken<ActionReducerMap<State, Action>>('Root reducers token', {factory: () => ({
router: fromRouter.routerReducer,
drawerNavigationItems: fromDrawer.reducer,
}),
});
export const metaReducers: MetaReducer<State>[] = !environment.production ? [] : [];
export const selectRouter = createFeatureSelector<
State,
fromRouter.RouterReducerState<any>
>('router');
const {
selectQueryParams, // select the current route query params
selectQueryParam, // factory function to select a query param
selectRouteParams, // select the current route params
selectRouteParam, // factory function to select a route param
selectRouteData, // select the current route data
selectUrl, // select the current url
} = fromRouter.getSelectors(selectRouter);
export const selectRouteId = selectRouteParam('id');
// export const selectStatus = selectQueryParam('status');
// Data
export const communitySelectors = new EntitySelectorsFactory().create<Community>('Community');
export const selectCommunityByRouteId = createSelector(
selectRouteId,
communitySelectors.selectEntities,
(id, communities) => communities.find(c => c.id === id)
);
// Drawer
export const selectDrawerNavigationItems = (state: State) => state.drawerNavigationItems.items as NavigationItem[];
You create a selector for the Community model with
export const communitySelectors = new EntitySelectorsFactory().create<Community>('Community');
and then you combine those two and return 1 Community
by the route id.
export const selectCommunityByRouteId = createSelector(
selectRouteId,
communitySelectors.selectEntities,
(id, communities) => communities.find(c => c.id === id)
);
really simple, you pick the input streams, provide a projection function and return the result.
Later, in the component
export class OneCommunityComponent implements OnInit {
community$: Observable<Community>;
constructor(
private store: Store<State>,
) {
this.community$ = this.store.select(selectCommunityByRouteId);
}
}
回答3:
The best solution in your situation it's adapters is very nice. You can look just here : https://ngrx.io/guide/entity/adapter
You can remove, add, update every object in your store with that and very easily. You just need to extend your state :)
来源:https://stackoverflow.com/questions/58596540/create-a-selector-for-a-ngrx-data-entity