Skip to content

Commit 511b7cf

Browse files
authored
feat(router-store): add provideRouterStore function (#3532)
Closes #3528
1 parent 5639c1e commit 511b7cf

File tree

9 files changed

+423
-355
lines changed

9 files changed

+423
-355
lines changed

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

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,24 @@
11
import { TestBed } from '@angular/core/testing';
2-
import { Router, RouterEvent, NavigationEnd } from '@angular/router';
2+
import { NavigationEnd, Router, RouterEvent } from '@angular/router';
33
import {
4+
FullRouterStateSerializer,
5+
MinimalRouterStateSerializer,
6+
RouterAction,
47
routerReducer,
58
RouterReducerState,
6-
StoreRouterConnectingModule,
7-
RouterAction,
89
RouterState,
910
RouterStateSerializer,
10-
MinimalRouterStateSerializer,
11-
FullRouterStateSerializer,
1211
} from '@ngrx/router-store';
13-
import { select, Store, ActionsSubject } from '@ngrx/store';
14-
import { withLatestFrom, filter, skip } from 'rxjs/operators';
12+
import { ActionsSubject, select, Store } from '@ngrx/store';
13+
import { filter, withLatestFrom } from 'rxjs/operators';
1514

1615
import { createTestModule } from './utils';
16+
import { StoreRouterConnectingService } from '../src/store_router_connecting.service';
1717

1818
describe('Router Store Module', () => {
1919
describe('with defining state key', () => {
2020
const customStateKey = 'router-reducer';
21-
let storeRouterConnectingModule: StoreRouterConnectingModule;
21+
let storeRouterConnectingService: StoreRouterConnectingService;
2222
let store: Store<State>;
2323
let router: Router;
2424

@@ -38,11 +38,13 @@ describe('Router Store Module', () => {
3838

3939
store = TestBed.inject(Store);
4040
router = TestBed.inject(Router);
41-
storeRouterConnectingModule = TestBed.inject(StoreRouterConnectingModule);
41+
storeRouterConnectingService = TestBed.inject(
42+
StoreRouterConnectingService
43+
);
4244
});
4345

4446
it('should have custom state key as own property', () => {
45-
expect((<any>storeRouterConnectingModule).stateKey).toBe(customStateKey);
47+
expect((<any>storeRouterConnectingService).stateKey).toBe(customStateKey);
4648
});
4749

4850
it('should call navigateIfNeeded with args selected by custom state key', (done: any) => {
@@ -54,7 +56,7 @@ describe('Router Store Module', () => {
5456
});
5557

5658
spyOn(
57-
storeRouterConnectingModule,
59+
storeRouterConnectingService,
5860
'navigateIfNeeded' as never
5961
).and.callThrough();
6062
logs = [];
@@ -63,7 +65,7 @@ describe('Router Store Module', () => {
6365
// and store emits its payload.
6466
router.navigateByUrl('/').then(() => {
6567
const actual = (<any>(
66-
storeRouterConnectingModule
68+
storeRouterConnectingService
6769
)).navigateIfNeeded.calls.allArgs();
6870

6971
expect(actual.length).toBe(1);
@@ -77,7 +79,7 @@ describe('Router Store Module', () => {
7779
const customStateKey = 'routerReducer';
7880
const customStateSelector = (state: State) => state.routerReducer;
7981

80-
let storeRouterConnectingModule: StoreRouterConnectingModule;
82+
let storeRouterConnectingService: StoreRouterConnectingService;
8183
let store: Store<State>;
8284
let router: Router;
8385

@@ -97,11 +99,13 @@ describe('Router Store Module', () => {
9799

98100
store = TestBed.inject(Store);
99101
router = TestBed.inject(Router);
100-
storeRouterConnectingModule = TestBed.inject(StoreRouterConnectingModule);
102+
storeRouterConnectingService = TestBed.inject(
103+
StoreRouterConnectingService
104+
);
101105
});
102106

103107
it('should have same state selector as own property', () => {
104-
expect((<any>storeRouterConnectingModule).stateKey).toBe(
108+
expect((<any>storeRouterConnectingService).stateKey).toBe(
105109
customStateSelector
106110
);
107111
});
@@ -115,7 +119,7 @@ describe('Router Store Module', () => {
115119
});
116120

117121
spyOn(
118-
storeRouterConnectingModule,
122+
storeRouterConnectingService,
119123
'navigateIfNeeded' as never
120124
).and.callThrough();
121125
logs = [];
@@ -124,7 +128,7 @@ describe('Router Store Module', () => {
124128
// and store emits its payload.
125129
router.navigateByUrl('/').then(() => {
126130
const actual = (<any>(
127-
storeRouterConnectingModule
131+
storeRouterConnectingService
128132
)).navigateIfNeeded.calls.allArgs();
129133

130134
expect(actual.length).toBe(1);

modules/router-store/src/index.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,15 @@ export {
2222
routerRequestAction,
2323
} from './actions';
2424
export { routerReducer, RouterReducerState } from './reducer';
25+
export { StoreRouterConnectingModule } from './router_store_module';
2526
export {
2627
StateKeyOrSelector,
27-
StoreRouterConnectingModule,
2828
StoreRouterConfig,
2929
NavigationActionTiming,
3030
ROUTER_CONFIG,
3131
DEFAULT_ROUTER_FEATURENAME,
3232
RouterState,
33-
} from './router_store_module';
33+
} from './router_store_config';
3434
export {
3535
RouterStateSerializer,
3636
BaseRouterStoreState,
@@ -45,3 +45,4 @@ export {
4545
MinimalRouterStateSerializer,
4646
} from './serializers/minimal_serializer';
4747
export { getSelectors, createRouterSelector } from './router_selectors';
48+
export { provideRouterStore } from './provide_router_store';

modules/router-store/src/models.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Params, Data } from '@angular/router';
1+
import { Data, Params } from '@angular/router';
22
import { MemoizedSelector } from '@ngrx/store';
33

44
export interface RouterStateSelectors<V> {
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { ENVIRONMENT_INITIALIZER, inject, Provider } from '@angular/core';
2+
import {
3+
_createRouterConfig,
4+
_ROUTER_CONFIG,
5+
ROUTER_CONFIG,
6+
RouterState,
7+
StoreRouterConfig,
8+
} from './router_store_config';
9+
import {
10+
FullRouterStateSerializer,
11+
SerializedRouterStateSnapshot,
12+
} from './serializers/full_serializer';
13+
import { MinimalRouterStateSerializer } from './serializers/minimal_serializer';
14+
import {
15+
BaseRouterStoreState,
16+
RouterStateSerializer,
17+
} from './serializers/base';
18+
import { StoreRouterConnectingService } from './store_router_connecting.service';
19+
import { EnvironmentProviders } from '@ngrx/store';
20+
21+
/**
22+
* Connects the Angular Router to the Store.
23+
*
24+
* @usageNotes
25+
*
26+
* ```typescript
27+
* bootstrapApplication(AppComponent, {
28+
* providers: [
29+
* provideRouterStore()
30+
* ]
31+
* })
32+
* ```
33+
*/
34+
export function provideRouterStore<
35+
T extends BaseRouterStoreState = SerializedRouterStateSnapshot
36+
>(config: StoreRouterConfig<T> = {}): EnvironmentProviders {
37+
return {
38+
ɵproviders: [
39+
{ provide: _ROUTER_CONFIG, useValue: config },
40+
{
41+
provide: ROUTER_CONFIG,
42+
useFactory: _createRouterConfig,
43+
deps: [_ROUTER_CONFIG],
44+
},
45+
{
46+
provide: RouterStateSerializer,
47+
useClass: config.serializer
48+
? config.serializer
49+
: config.routerState === RouterState.Full
50+
? FullRouterStateSerializer
51+
: MinimalRouterStateSerializer,
52+
},
53+
{
54+
provide: ENVIRONMENT_INITIALIZER,
55+
multi: true,
56+
useFactory() {
57+
return () => inject(StoreRouterConnectingService);
58+
},
59+
},
60+
StoreRouterConnectingService,
61+
],
62+
};
63+
}

modules/router-store/src/router_selectors.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
} from '@ngrx/store';
66
import { RouterStateSelectors } from './models';
77
import { RouterReducerState } from './reducer';
8-
import { DEFAULT_ROUTER_FEATURENAME } from './router_store_module';
8+
import { DEFAULT_ROUTER_FEATURENAME } from './router_store_config';
99

1010
export function createRouterSelector<
1111
State extends Record<string, any>
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import { InjectionToken } from '@angular/core';
2+
import { Selector } from '@ngrx/store';
3+
import { RouterReducerState } from './reducer';
4+
import {
5+
BaseRouterStoreState,
6+
RouterStateSerializer,
7+
} from './serializers/base';
8+
import { SerializedRouterStateSnapshot } from './serializers/full_serializer';
9+
import { MinimalRouterStateSerializer } from './serializers/minimal_serializer';
10+
11+
export type StateKeyOrSelector<
12+
T extends BaseRouterStoreState = SerializedRouterStateSnapshot
13+
> = string | Selector<any, RouterReducerState<T>>;
14+
15+
export enum NavigationActionTiming {
16+
PreActivation = 1,
17+
PostActivation = 2,
18+
}
19+
export const DEFAULT_ROUTER_FEATURENAME = 'router';
20+
21+
export const _ROUTER_CONFIG = new InjectionToken(
22+
'@ngrx/router-store Internal Configuration'
23+
);
24+
export const ROUTER_CONFIG = new InjectionToken(
25+
'@ngrx/router-store Configuration'
26+
);
27+
28+
/**
29+
* Minimal = Serializes the router event with MinimalRouterStateSerializer
30+
* Full = Serializes the router event with FullRouterStateSerializer
31+
*/
32+
export const enum RouterState {
33+
Full,
34+
Minimal,
35+
}
36+
37+
export function _createRouterConfig(
38+
config: StoreRouterConfig
39+
): StoreRouterConfig {
40+
return {
41+
stateKey: DEFAULT_ROUTER_FEATURENAME,
42+
serializer: MinimalRouterStateSerializer,
43+
navigationActionTiming: NavigationActionTiming.PreActivation,
44+
...config,
45+
};
46+
}
47+
48+
export interface StoreRouterConfig<
49+
T extends BaseRouterStoreState = SerializedRouterStateSnapshot
50+
> {
51+
stateKey?: StateKeyOrSelector<T>;
52+
serializer?: new (...args: any[]) => RouterStateSerializer;
53+
/**
54+
* By default, ROUTER_NAVIGATION is dispatched before guards and resolvers run.
55+
* Therefore, the action could run too soon, for example
56+
* there may be a navigation cancel due to a guard saying the navigation is not allowed.
57+
* To run ROUTER_NAVIGATION after guards and resolvers,
58+
* set this property to NavigationActionTiming.PostActivation.
59+
*/
60+
navigationActionTiming?: NavigationActionTiming;
61+
/**
62+
* Decides which router serializer should be used, if there is none provided, and the metadata on the dispatched @ngrx/router-store action payload.
63+
* Set to `Minimal` to use the `MinimalRouterStateSerializer` and to set a minimal router event with the navigation id and url as payload.
64+
* Set to `Full` to use the `FullRouterStateSerializer` and to set the angular router events as payload.
65+
*/
66+
routerState?: RouterState;
67+
}

0 commit comments

Comments
 (0)