Skip to content

Commit

Permalink
feat(router-store): add selectors for router state (#1874)
Browse files Browse the repository at this point in the history
Closes #1854
  • Loading branch information
jasonhodges authored and brandonroberts committed May 29, 2019
1 parent d5e3c0c commit 21c67cc
Show file tree
Hide file tree
Showing 6 changed files with 260 additions and 0 deletions.
164 changes: 164 additions & 0 deletions modules/router-store/spec/router_selectors.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
import { RouterReducerState, getSelectors } from '@ngrx/router-store';
import { RouterStateSelectors } from '../src/models';

const mockData = {
state: {
root: {
params: {},
paramMap: {
params: {},
},
data: {},
url: [],
outlet: 'primary',
routeConfig: null,
queryParams: {},
queryParamMap: {
params: {},
},
fragment: null,
firstChild: {
params: {},
paramMap: {
params: {},
},
data: {},
url: [
{
path: 'login',
parameters: {},
},
],
outlet: 'primary',
routeConfig: {
path: 'login',
},
queryParams: {
id: 3,
},
queryParamMap: {
params: {},
},
firstChild: {
params: {
id: 'etyDDwAAQBAJ',
},
paramMap: {
params: {
id: 'etyDDwAAQBAJ',
},
},
data: {
testData: 'test-data',
},
url: [
{
path: 'etyDDwAAQBAJ',
parameters: {},
},
],
outlet: 'primary',
routeConfig: {
path: ':id',
},
queryParams: {},
queryParamMap: {
params: {},
},
children: [],
},
fragment: null,
children: [],
},
children: [
{
params: {},
paramMap: {
params: {},
},
data: {},
url: [
{
path: 'login',
parameters: {},
},
],
outlet: 'primary',
routeConfig: {
path: 'login',
},
queryParams: {},
queryParamMap: {
params: {},
},
children: [],
},
],
},
url: '/login',
},
navigationId: 1,
};
describe('Router State Selectors', () => {
describe('Composed Selectors', () => {
interface State {
router: RouterReducerState<any>;
}

let selectors: RouterStateSelectors<State>;
let state: State;

beforeEach(() => {
state = {
router: mockData,
};

selectors = getSelectors((state: State) => state.router);
});
it('should create selectCurrentRoute selector for selecting the current route', () => {
const result = selectors.selectCurrentRoute(state);

expect(result).toEqual(state.router.state.root.firstChild.firstChild);
});
it('should return undefined from selectCurrentRoute if routerState does not exist', () => {
interface State {
router: any;
}
let state: State;
state = {
router: undefined,
};
selectors = getSelectors((state: State) => state.router);

const result = selectors.selectCurrentRoute(state);

expect(result).toEqual(undefined);
});
it('should create a selector for selecting the query params', () => {
const result = selectors.selectQueryParams(state);

expect(result).toEqual(
state.router.state.root.firstChild.firstChild.queryParams
);
});
it('should create a selector for selecting the route params', () => {
const result = selectors.selectRouteParams(state);

expect(result).toEqual(
state.router.state.root.firstChild.firstChild.params
);
});
it('should create a selector for selecting the route data', () => {
const result = selectors.selectRouteData(state);

expect(result).toEqual(
state.router.state.root.firstChild.firstChild.data
);
});
it('should create a selector for selecting the url', () => {
const result = selectors.selectUrl(state);

expect(result).toEqual(state.router.state.url);
});
});
});
1 change: 1 addition & 0 deletions modules/router-store/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,4 @@ export {
MinimalRouterStateSnapshot,
MinimalRouterStateSerializer,
} from './serializers';
export { getSelectors } from './router_selectors';
9 changes: 9 additions & 0 deletions modules/router-store/src/models.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Data, Params } from '@angular/router';

export interface RouterStateSelectors<V> {
selectCurrentRoute: (state: V) => any;
selectQueryParams: (state: V) => Params;
selectRouteParams: (state: V) => Params;
selectRouteData: (state: V) => Data;
selectUrl: (state: V) => string;
}
49 changes: 49 additions & 0 deletions modules/router-store/src/router_selectors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { RouterReducerState } from '@ngrx/router-store';
import { createSelector } from '@ngrx/store';
import { RouterStateSelectors } from './models';

export function getSelectors<V>(
selectState: (state: V) => RouterReducerState<any>
): RouterStateSelectors<V>;
export function getSelectors<V>(
selectState: (state: V) => RouterReducerState<any>
): RouterStateSelectors<V> {
const selectRouterState = createSelector(
selectState,
router => router && router.state
);
const selectCurrentRoute = createSelector(selectRouterState, routerState => {
if (!routerState) {
return undefined;
}
let route = routerState.root;
while (route.firstChild) {
route = route.firstChild;
}
return route;
});
const selectQueryParams = createSelector(
selectCurrentRoute,
route => route && route.queryParams
);
const selectRouteParams = createSelector(
selectCurrentRoute,
route => route && route.params
);
const selectRouteData = createSelector(
selectCurrentRoute,
route => route && route.data
);
const selectUrl = createSelector(
selectRouterState,
routerState => routerState && routerState.url
);

return {
selectCurrentRoute,
selectQueryParams,
selectRouteParams,
selectRouteData,
selectUrl,
};
}
33 changes: 33 additions & 0 deletions projects/ngrx.io/content/guide/router-store/selectors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Router selectors

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

The `getSelectors` method takes a selector function as its only argument to select the piece of state where the router state is being stored.
The example below shows how to provide a selector for the top level `router` key in your state object.

**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.

Usage:

`reducers/index.ts`

```ts
import { getSelectors, RouterReducerState } from '@ngrx/router-store';

export interface State {
router: fromRouter.RouterReducerState<any>;
}

export const selectRouter = createFeatureSelector<
State,
fromRouter.RouterReducerState<any>
>('router');

const {
selectQueryParams, // select the current route query params
selectRouteParams, // select the current route params
selectRouteData, // select the current route data
selectUrl, // select the current url
} = getSelectors(selectRouter);

```
4 changes: 4 additions & 0 deletions projects/ngrx.io/content/navigation.json
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,10 @@
"title": "Actions",
"url": "guide/router-store/actions"
},
{
"title": "Selectors",
"url": "guide/router-store/selectors"
},
{
"title": "Configuration",
"url": "guide/router-store/configuration"
Expand Down

0 comments on commit 21c67cc

Please sign in to comment.