Skip to content

Commit 21c67cc

Browse files
jasonhodgesbrandonroberts
authored andcommitted
feat(router-store): add selectors for router state (#1874)
Closes #1854
1 parent d5e3c0c commit 21c67cc

File tree

6 files changed

+260
-0
lines changed

6 files changed

+260
-0
lines changed
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
import { RouterReducerState, getSelectors } from '@ngrx/router-store';
2+
import { RouterStateSelectors } from '../src/models';
3+
4+
const mockData = {
5+
state: {
6+
root: {
7+
params: {},
8+
paramMap: {
9+
params: {},
10+
},
11+
data: {},
12+
url: [],
13+
outlet: 'primary',
14+
routeConfig: null,
15+
queryParams: {},
16+
queryParamMap: {
17+
params: {},
18+
},
19+
fragment: null,
20+
firstChild: {
21+
params: {},
22+
paramMap: {
23+
params: {},
24+
},
25+
data: {},
26+
url: [
27+
{
28+
path: 'login',
29+
parameters: {},
30+
},
31+
],
32+
outlet: 'primary',
33+
routeConfig: {
34+
path: 'login',
35+
},
36+
queryParams: {
37+
id: 3,
38+
},
39+
queryParamMap: {
40+
params: {},
41+
},
42+
firstChild: {
43+
params: {
44+
id: 'etyDDwAAQBAJ',
45+
},
46+
paramMap: {
47+
params: {
48+
id: 'etyDDwAAQBAJ',
49+
},
50+
},
51+
data: {
52+
testData: 'test-data',
53+
},
54+
url: [
55+
{
56+
path: 'etyDDwAAQBAJ',
57+
parameters: {},
58+
},
59+
],
60+
outlet: 'primary',
61+
routeConfig: {
62+
path: ':id',
63+
},
64+
queryParams: {},
65+
queryParamMap: {
66+
params: {},
67+
},
68+
children: [],
69+
},
70+
fragment: null,
71+
children: [],
72+
},
73+
children: [
74+
{
75+
params: {},
76+
paramMap: {
77+
params: {},
78+
},
79+
data: {},
80+
url: [
81+
{
82+
path: 'login',
83+
parameters: {},
84+
},
85+
],
86+
outlet: 'primary',
87+
routeConfig: {
88+
path: 'login',
89+
},
90+
queryParams: {},
91+
queryParamMap: {
92+
params: {},
93+
},
94+
children: [],
95+
},
96+
],
97+
},
98+
url: '/login',
99+
},
100+
navigationId: 1,
101+
};
102+
describe('Router State Selectors', () => {
103+
describe('Composed Selectors', () => {
104+
interface State {
105+
router: RouterReducerState<any>;
106+
}
107+
108+
let selectors: RouterStateSelectors<State>;
109+
let state: State;
110+
111+
beforeEach(() => {
112+
state = {
113+
router: mockData,
114+
};
115+
116+
selectors = getSelectors((state: State) => state.router);
117+
});
118+
it('should create selectCurrentRoute selector for selecting the current route', () => {
119+
const result = selectors.selectCurrentRoute(state);
120+
121+
expect(result).toEqual(state.router.state.root.firstChild.firstChild);
122+
});
123+
it('should return undefined from selectCurrentRoute if routerState does not exist', () => {
124+
interface State {
125+
router: any;
126+
}
127+
let state: State;
128+
state = {
129+
router: undefined,
130+
};
131+
selectors = getSelectors((state: State) => state.router);
132+
133+
const result = selectors.selectCurrentRoute(state);
134+
135+
expect(result).toEqual(undefined);
136+
});
137+
it('should create a selector for selecting the query params', () => {
138+
const result = selectors.selectQueryParams(state);
139+
140+
expect(result).toEqual(
141+
state.router.state.root.firstChild.firstChild.queryParams
142+
);
143+
});
144+
it('should create a selector for selecting the route params', () => {
145+
const result = selectors.selectRouteParams(state);
146+
147+
expect(result).toEqual(
148+
state.router.state.root.firstChild.firstChild.params
149+
);
150+
});
151+
it('should create a selector for selecting the route data', () => {
152+
const result = selectors.selectRouteData(state);
153+
154+
expect(result).toEqual(
155+
state.router.state.root.firstChild.firstChild.data
156+
);
157+
});
158+
it('should create a selector for selecting the url', () => {
159+
const result = selectors.selectUrl(state);
160+
161+
expect(result).toEqual(state.router.state.url);
162+
});
163+
});
164+
});

modules/router-store/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,4 @@ export {
3535
MinimalRouterStateSnapshot,
3636
MinimalRouterStateSerializer,
3737
} from './serializers';
38+
export { getSelectors } from './router_selectors';

modules/router-store/src/models.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { Data, Params } from '@angular/router';
2+
3+
export interface RouterStateSelectors<V> {
4+
selectCurrentRoute: (state: V) => any;
5+
selectQueryParams: (state: V) => Params;
6+
selectRouteParams: (state: V) => Params;
7+
selectRouteData: (state: V) => Data;
8+
selectUrl: (state: V) => string;
9+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { RouterReducerState } from '@ngrx/router-store';
2+
import { createSelector } from '@ngrx/store';
3+
import { RouterStateSelectors } from './models';
4+
5+
export function getSelectors<V>(
6+
selectState: (state: V) => RouterReducerState<any>
7+
): RouterStateSelectors<V>;
8+
export function getSelectors<V>(
9+
selectState: (state: V) => RouterReducerState<any>
10+
): RouterStateSelectors<V> {
11+
const selectRouterState = createSelector(
12+
selectState,
13+
router => router && router.state
14+
);
15+
const selectCurrentRoute = createSelector(selectRouterState, routerState => {
16+
if (!routerState) {
17+
return undefined;
18+
}
19+
let route = routerState.root;
20+
while (route.firstChild) {
21+
route = route.firstChild;
22+
}
23+
return route;
24+
});
25+
const selectQueryParams = createSelector(
26+
selectCurrentRoute,
27+
route => route && route.queryParams
28+
);
29+
const selectRouteParams = createSelector(
30+
selectCurrentRoute,
31+
route => route && route.params
32+
);
33+
const selectRouteData = createSelector(
34+
selectCurrentRoute,
35+
route => route && route.data
36+
);
37+
const selectUrl = createSelector(
38+
selectRouterState,
39+
routerState => routerState && routerState.url
40+
);
41+
42+
return {
43+
selectCurrentRoute,
44+
selectQueryParams,
45+
selectRouteParams,
46+
selectRouteData,
47+
selectUrl,
48+
};
49+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Router selectors
2+
3+
The `getSelectors` method supplied within `@ngrx/router-store` provides functions for selecting common information from the router state.
4+
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.
6+
The example below shows how to provide a selector for the top level `router` key in your state object.
7+
8+
**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.
9+
10+
Usage:
11+
12+
`reducers/index.ts`
13+
14+
```ts
15+
import { getSelectors, RouterReducerState } from '@ngrx/router-store';
16+
17+
export interface State {
18+
router: fromRouter.RouterReducerState<any>;
19+
}
20+
21+
export const selectRouter = createFeatureSelector<
22+
State,
23+
fromRouter.RouterReducerState<any>
24+
>('router');
25+
26+
const {
27+
selectQueryParams, // select the current route query params
28+
selectRouteParams, // select the current route params
29+
selectRouteData, // select the current route data
30+
selectUrl, // select the current url
31+
} = getSelectors(selectRouter);
32+
33+
```

projects/ngrx.io/content/navigation.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,10 @@
192192
"title": "Actions",
193193
"url": "guide/router-store/actions"
194194
},
195+
{
196+
"title": "Selectors",
197+
"url": "guide/router-store/selectors"
198+
},
195199
{
196200
"title": "Configuration",
197201
"url": "guide/router-store/configuration"

0 commit comments

Comments
 (0)