Skip to content

Commit

Permalink
feat(RouterStore): Add configurable option for router reducer name (#417
Browse files Browse the repository at this point in the history
)

Closes #410
  • Loading branch information
phillipzada authored and brandonroberts committed Oct 23, 2017
1 parent 4db7d61 commit ab7de5c
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 10 deletions.
56 changes: 55 additions & 1 deletion modules/router-store/spec/integration.spec.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { StoreRouterConfig } from '../src/router_store_module';
import { Component, Provider } from '@angular/core';
import { TestBed } from '@angular/core/testing';
import { NavigationEnd, Router, RouterStateSnapshot } from '@angular/router';
Expand Down Expand Up @@ -457,7 +458,59 @@ describe('integration spec', () => {
{ type: 'router', event: 'GuardsCheckEnd', url: '/next' },
{ type: 'router', event: 'ResolveStart', url: '/next' },
{ type: 'router', event: 'ResolveEnd', url: '/next' },
{ type: 'router', event: 'NavigationEnd', url: '/next' },
]);

done();
});
});

it('should work when defining state key', (done: any) => {
const reducer = (state: string = '', action: RouterAction<any>) => {
if (action.type === ROUTER_NAVIGATION) {
return action.payload.routerState.url.toString();
} else {
return state;
}
};

createTestModule({
reducers: { reducer },
config: { stateKey: 'router-reducer' },
});

const router: Router = TestBed.get(Router);
const store = TestBed.get(Store);
const log = logOfRouterAndStore(router, store);

router
.navigateByUrl('/')
.then(() => {
expect(log).toEqual([
{ type: 'store', state: '' }, // init event. has nothing to do with the router
{ type: 'router', event: 'NavigationStart', url: '/' },
{ type: 'router', event: 'RoutesRecognized', url: '/' },
{ type: 'store', state: '/' }, // ROUTER_NAVIGATION event in the store
{ type: 'router', event: 'GuardsCheckStart', url: '/' },
{ type: 'router', event: 'GuardsCheckEnd', url: '/' },
{ type: 'router', event: 'ResolveStart', url: '/' },
{ type: 'router', event: 'ResolveEnd', url: '/' },
{ type: 'router', event: 'NavigationEnd', url: '/' },
]);
})
.then(() => {
log.splice(0);
return router.navigateByUrl('next');
})
.then(() => {
expect(log).toEqual([
{ type: 'router', event: 'NavigationStart', url: '/next' },
{ type: 'router', event: 'RoutesRecognized', url: '/next' },
{ type: 'store', state: '/next' },
{ type: 'router', event: 'GuardsCheckStart', url: '/next' },
{ type: 'router', event: 'GuardsCheckEnd', url: '/next' },
{ type: 'router', event: 'ResolveStart', url: '/next' },
{ type: 'router', event: 'ResolveEnd', url: '/next' },
{ type: 'router', event: 'NavigationEnd', url: '/next' },
]);

Expand All @@ -472,6 +525,7 @@ function createTestModule(
canActivate?: Function;
canLoad?: Function;
providers?: Provider[];
config?: StoreRouterConfig;
} = {}
) {
@Component({
Expand Down Expand Up @@ -503,7 +557,7 @@ function createTestModule(
canLoad: ['CanLoadNext'],
},
]),
StoreRouterConnectingModule,
StoreRouterConnectingModule.forRoot(opts.config),
],
providers: [
{
Expand Down
4 changes: 4 additions & 0 deletions modules/router-store/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ export {
RouterCancelPayload,
RouterNavigationPayload,
StoreRouterConnectingModule,
StoreRouterConfig,
StoreRouterConfigFunction,
ROUTER_CONFIG,
DEFAULT_ROUTER_FEATURENAME,
} from './router_store_module';

export {
Expand Down
81 changes: 73 additions & 8 deletions modules/router-store/src/router_store_module.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { NgModule } from '@angular/core';
import {
NgModule,
ModuleWithProviders,
InjectionToken,
Inject,
} from '@angular/core';
import {
NavigationCancel,
NavigationError,
Expand Down Expand Up @@ -107,6 +112,33 @@ export function routerReducer<T = RouterStateSnapshot>(
}
}

export type StoreRouterConfig = {
stateKey?: string;
};

export const _ROUTER_CONFIG = new InjectionToken(
'@ngrx/router-store Internal Configuration'
);
export const ROUTER_CONFIG = new InjectionToken(
'@ngrx/router-store Configuration'
);
export const DEFAULT_ROUTER_FEATURENAME = 'routerReducer';

export function _createDefaultRouterConfig(config: any): StoreRouterConfig {
let _config = {};

if (typeof config === 'function') {
_config = config();
}

return {
stateKey: DEFAULT_ROUTER_FEATURENAME,
..._config,
};
}

export type StoreRouterConfigFunction = () => StoreRouterConfig;

/**
* Connects RouterModule with StoreModule.
*
Expand Down Expand Up @@ -152,21 +184,54 @@ export function routerReducer<T = RouterStateSnapshot>(
@NgModule({
providers: [
{ provide: RouterStateSerializer, useClass: DefaultRouterStateSerializer },
{
provide: _ROUTER_CONFIG,
useValue: { stateKey: DEFAULT_ROUTER_FEATURENAME },
},
{
provide: ROUTER_CONFIG,
useFactory: _createDefaultRouterConfig,
deps: [_ROUTER_CONFIG],
},
],
})
export class StoreRouterConnectingModule {
static forRoot(
config?: StoreRouterConfig | StoreRouterConfigFunction
): ModuleWithProviders;
static forRoot(
config: StoreRouterConfig | StoreRouterConfigFunction = {}
): ModuleWithProviders {
return {
ngModule: StoreRouterConnectingModule,
providers: [
{ provide: _ROUTER_CONFIG, useValue: config },
{
provide: ROUTER_CONFIG,
useFactory: _createDefaultRouterConfig,
deps: [_ROUTER_CONFIG],
},
],
};
}

private routerState: RouterStateSnapshot;
private storeState: any;
private lastRoutesRecognized: RoutesRecognized;

private dispatchTriggeredByRouter: boolean = false; // used only in dev mode in combination with routerReducer
private navigationTriggeredByDispatch: boolean = false; // used only in dev mode in combination with routerReducer

private stateKey: string;

constructor(
private store: Store<any>,
private router: Router,
private serializer: RouterStateSerializer<RouterStateSnapshot>
private serializer: RouterStateSerializer<RouterStateSnapshot>,
@Inject(ROUTER_CONFIG) private config: StoreRouterConfig
) {
this.stateKey = this.config.stateKey as string;

this.setUpBeforePreactivationHook();
this.setUpStoreStateListener();
this.setUpStateRollbackEvents();
Expand All @@ -187,28 +252,28 @@ export class StoreRouterConnectingModule {
this.store.subscribe(s => {
this.storeState = s;
});
this.store.select('routerReducer').subscribe(() => {
this.store.select(this.stateKey).subscribe(() => {
this.navigateIfNeeded();
});
}

private shouldDispatchRouterNavigation(): boolean {
if (!this.storeState['routerReducer']) return true;
if (!this.storeState[this.stateKey]) return true;
return !this.navigationTriggeredByDispatch;
}

private navigateIfNeeded(): void {
if (
!this.storeState['routerReducer'] ||
!this.storeState['routerReducer'].state
!this.storeState[this.stateKey] ||
!this.storeState[this.stateKey].state
) {
return;
}
if (this.dispatchTriggeredByRouter) return;

if (this.router.url !== this.storeState['routerReducer'].state.url) {
if (this.router.url !== this.storeState[this.stateKey].state.url) {
this.navigationTriggeredByDispatch = true;
this.router.navigateByUrl(this.storeState['routerReducer'].state.url);
this.router.navigateByUrl(this.storeState[this.stateKey].state.url);
}
}

Expand Down
2 changes: 1 addition & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -7873,4 +7873,4 @@ zone.js@^0.8.12:

zone.js@^0.8.14:
version "0.8.18"
resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.8.18.tgz#8cecb3977fcd1b3090562ff4570e2847e752b48d"
resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.8.18.tgz#8cecb3977fcd1b3090562ff4570e2847e752b48d"

0 comments on commit ab7de5c

Please sign in to comment.