Skip to content

Commit 507f58e

Browse files
feat(router-store): add createRouterSelector to select router data for default config (#3103)
1 parent 23c846b commit 507f58e

File tree

5 files changed

+74
-44
lines changed

5 files changed

+74
-44
lines changed

modules/router-store/spec/router_selectors.spec.ts

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
import { RouterReducerState, getSelectors } from '@ngrx/router-store';
1+
import {
2+
getSelectors,
3+
RouterReducerState,
4+
DEFAULT_ROUTER_FEATURENAME,
5+
createRouterSelector,
6+
} from '@ngrx/router-store';
27
import { RouterStateSelectors } from '../src/models';
38

49
const mockData = {
@@ -130,7 +135,7 @@ describe('Router State Selectors', () => {
130135
router: mockData,
131136
};
132137

133-
selectors = getSelectors((state: State) => state.router);
138+
selectors = getSelectors();
134139
});
135140

136141
it('should create selectCurrentRoute selector for selecting the current route', () => {
@@ -139,6 +144,33 @@ describe('Router State Selectors', () => {
139144
expect(result).toEqual(state.router.state.root.firstChild.firstChild);
140145
});
141146

147+
it('should be able to overwrite default router feature state name', () => {
148+
const stateOverwrite = {
149+
anotherRouterKey: mockData,
150+
};
151+
const selectorOverwrite = getSelectors(
152+
(state: typeof stateOverwrite) => state.anotherRouterKey
153+
);
154+
155+
const result = selectorOverwrite.selectCurrentRoute(stateOverwrite);
156+
expect(result).toEqual(
157+
stateOverwrite.anotherRouterKey.state.root.firstChild.firstChild
158+
);
159+
});
160+
161+
it('should be able to use DEFAULT_ROUTER_FEATURENAME and createRouterSelector to select router feature state', () => {
162+
const stateOverwrite = {
163+
[DEFAULT_ROUTER_FEATURENAME]: mockData,
164+
};
165+
const selectorOverwrite = getSelectors(createRouterSelector());
166+
167+
const result = selectorOverwrite.selectCurrentRoute(stateOverwrite);
168+
expect(result).toEqual(
169+
stateOverwrite[DEFAULT_ROUTER_FEATURENAME].state.root.firstChild
170+
.firstChild
171+
);
172+
});
173+
142174
it('should return undefined from selectCurrentRoute if routerState does not exist', () => {
143175
interface State {
144176
router: any;

modules/router-store/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,4 @@ export {
4444
MinimalRouterStateSnapshot,
4545
MinimalRouterStateSerializer,
4646
} from './serializers/minimal_serializer';
47-
export { getSelectors } from './router_selectors';
47+
export { getSelectors, createRouterSelector } from './router_selectors';

modules/router-store/src/router_selectors.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,21 @@
1-
import { createSelector } from '@ngrx/store';
1+
import {
2+
createFeatureSelector,
3+
createSelector,
4+
MemoizedSelector,
5+
} from '@ngrx/store';
26
import { RouterStateSelectors } from './models';
37
import { RouterReducerState } from './reducer';
8+
import { DEFAULT_ROUTER_FEATURENAME } from './router_store_module';
9+
10+
export function createRouterSelector<State extends Record<string, any>>(): MemoizedSelector<
11+
State,
12+
RouterReducerState
13+
> {
14+
return createFeatureSelector(DEFAULT_ROUTER_FEATURENAME);
15+
}
416

517
export function getSelectors<V>(
6-
selectState: (state: V) => RouterReducerState<any>
18+
selectState: (state: V) => RouterReducerState<any> = createRouterSelector<V>()
719
): RouterStateSelectors<V> {
820
const selectRouterState = createSelector(
921
selectState,

projects/example-app/src/app/reducers/index.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,4 @@ export const selectShowSidenav = createSelector(
8080
/**
8181
* Router Selectors
8282
*/
83-
export const selectRouter = createFeatureSelector<fromRouter.RouterReducerState>(
84-
'router'
85-
);
86-
87-
export const { selectRouteData } = fromRouter.getSelectors(selectRouter);
83+
export const { selectRouteData } = fromRouter.getSelectors();

projects/ngrx.io/content/guide/router-store/selectors.md

Lines changed: 24 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22

33
The `getSelectors` method supplied within `@ngrx/router-store` provides functions for selecting common information from the router state.
44

5-
The `getSelectors` method takes a selector function as its only argument to select the piece of state where the router state is being stored.
5+
The default behavior of `getSelectors` selects the router state for the `router` state key.
6+
If the default router state config is overwritten with a different router state key, the `getSelectors` method takes a selector function to select the piece of state where the router state is being stored.
67
The example below shows how to provide a selector for the top level `router` key in your state object.
78

89
**Note:** The `getSelectors` method works with the `routerReducer` provided by `@ngrx/router-store`. If you use a [custom serializer](guide/router-store/configuration#custom-router-state-serializer), you'll need to provide your own selectors.
@@ -13,13 +14,8 @@ Usage:
1314

1415
[Full App Used In This Example](https://stackblitz.com/edit/ngrx-router-store-selectors?file=src/app/car.state.ts)
1516

16-
`router.selectors.ts`
17-
18-
```ts
19-
import { getSelectors, RouterReducerState } from '@ngrx/router-store';
20-
import { createFeatureSelector } from '@ngrx/store';
21-
22-
export const selectRouter = createFeatureSelector<RouterReducerState>('router');
17+
<code-example header="router.selectors.ts">
18+
import { getSelectors } from '@ngrx/router-store';
2319

2420
export const {
2521
selectCurrentRoute, // select the current route
@@ -30,12 +26,10 @@ export const {
3026
selectRouteParam, // factory function to select a route param
3127
selectRouteData, // select the current route data
3228
selectUrl, // select the current url
33-
} = getSelectors(selectRouter);
34-
```
29+
} = getSelectors();
30+
</code-example>
3531

36-
`car.reducer.ts`
37-
38-
```ts
32+
<code-example header="car.reducer.ts" >
3933
import { createReducer, on } from '@ngrx/store';
4034
import { EntityState, createEntityAdapter } from '@ngrx/entity';
4135
import { appInit } from './car.actions';
@@ -47,28 +41,26 @@ export interface Car {
4741
model: string;
4842
}
4943

50-
export type CarState = EntityState<Car>;
44+
export type CarState = EntityState&lt;Car&gt;;
5145

52-
export const carAdapter = createEntityAdapter<Car>({
53-
selectId: car => car.id,
46+
export const carAdapter = createEntityAdapter&lt;Car&gt;({
47+
selectId: car =&gt; car.id,
5448
});
5549

5650
const initialState = carAdapter.getInitialState();
5751

58-
export const reducer = createReducer<CarState>(
52+
export const reducer = createReducer&lt;CarState&gt;(
5953
initialState,
60-
on(appInit, (state, { cars }) => carAdapter.addMany(cars, state))
54+
on(appInit, (state, { cars }) =&gt; carAdapter.addMany(cars, state))
6155
);
62-
```
63-
64-
`car.selectors.ts`
56+
</code-example>
6557

66-
```ts
58+
<code-example header="car.selectors.ts" >
6759
import { createFeatureSelector, createSelector } from '@ngrx/store';
6860
import { selectRouteParams } from '../router.selectors';
6961
import { carAdapter, CarState } from './car.reducer';
7062

71-
export const carsFeatureSelector = createFeatureSelector<CarState>('cars');
63+
export const carsFeatureSelector = createFeatureSelector&lt;CarState&gt;('cars');
7264

7365
const { selectEntities, selectAll } = carAdapter.getSelectors();
7466

@@ -88,13 +80,11 @@ export const selectCars = createSelector(
8880
export const selectCar = createSelector(
8981
selectCarEntities,
9082
selectRouteParams,
91-
(cars, { carId }) => cars[carId]
83+
(cars, { carId }) =&gt; cars[carId]
9284
);
93-
```
94-
95-
`car.component.ts`
85+
</code-example>
9686

97-
```ts
87+
<code-example header="car.component.ts" >
9888
import { Component } from '@angular/core';
9989
import { select, Store } from '@ngrx/store';
10090
import { selectCar } from './car.selectors';
@@ -109,7 +99,7 @@ export class CarComponent {
10999

110100
constructor(private store: Store) {}
111101
}
112-
```
102+
</code-example>
113103

114104
## Extracting all params in the current route
115105

@@ -138,11 +128,11 @@ Using `selectRouteParam{s}` will get the `matched` param but not the `urlPath` p
138128

139129
If all params in the URL Tree need to be extracted (both `urlPath` and `matched`), the following custom selector can be used. It accumulates params of all the segments in the matched route:
140130

141-
```typescript
131+
<code-example>
142132
import { Params } from '@angular/router';
143133
import { createSelector } from '@ngrx/store';
144134

145-
export const selectRouteNestedParams = createSelector(selectRouter, (router) => {
135+
export const selectRouteNestedParams = createSelector(selectRouter, (router) =&gt; {
146136
let currentRoute = router?.state?.root;
147137
let params: Params = {};
148138
while (currentRoute?.firstChild) {
@@ -155,9 +145,9 @@ export const selectRouteNestedParams = createSelector(selectRouter, (router) =>
155145
return params;
156146
});
157147

158-
export const selectRouteNestedParam = (param: string) =>
159-
createSelector(selectRouteNestedParams, (params) => params && params[param]);
160-
```
148+
export const selectRouteNestedParam = (param: string) =&gt;
149+
createSelector(selectRouteNestedParams, (params) =&gt; params &amp;&amp; params[param]);
150+
</code-example>
161151

162152
<div class="alert is-important">
163153

0 commit comments

Comments
 (0)