Skip to content
This repository has been archived by the owner on Jul 27, 2018. It is now read-only.

Commit

Permalink
feat(router-store): Provide bindings to connect @angular/router
Browse files Browse the repository at this point in the history
With the deprecation of `@ngrx/router`, this library will provide bindings to connect `@angular/router` to  `@ngrx/store`.

BREAKING CHANGE: 

Router API has changed internally

BEFORE:

Use the `routerReducer` when providing `Store`:
  ```ts
  import { provideStore } from '@ngrx/store';
  import { routerReducer } from '@ngrx/router-store';
  
  
  export const storeProvider = provideStore({
    // Your reducers go here,
    router: routerReducer
  });
  ```

Install the bindings providers after you setup the router providers:
  ```ts
  import { connectRouterToStore } from '@ngrx/router-store';
  
  bootstrap(App, [
    storeProvider,
    provideRouter(routes),
    connectRouterToStore()
  ]);
  ```

AFTER:

Use the `routerReducer` when providing the `StoreModule.provideStore` and add the `RouterStoreModule.connectRouter()` to the `@NgModule.imports`:

  ```ts
  import { StoreModule } from '@ngrx/store';
  import { routerReducer, RouterStoreModule } from '@ngrx/router-store';

  @NgModule({
    imports: [
      BrowserModule,
      StoreModule.provideStore({ router: routerReducer }),
      RouterStoreModule.connectRouter()
    ],
    bootstrap: [ AppComponent ]
  })
  export class AppModule {
  }
  ```

BEFORE:

```ts
import {routerActions} from '@ngrx/store';

store.dispatch(routerActions.go('/path', 'query=string'));
store.dispatch(routerActions.replace('/path', 'query=string'));
store.dispatch(routerActions.search('query=string'));
```

AFTER:

```ts
import {routerActions} from '@ngrx/store';

store.dispatch(routerActions.go('/path', { query: 'string' ));
store.dispatch(routerActions.replace('/path', { query: 'string' ));
store.dispatch(routerActions.search({ query: 'string' ));
```

Closes #3
  • Loading branch information
brandonroberts authored and MikeRyanDev committed Aug 26, 2016
1 parent fac115b commit 2af0ed4
Show file tree
Hide file tree
Showing 14 changed files with 206 additions and 159 deletions.
35 changes: 15 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# @ngrx/router-store
### Bindings to connect ngrx/router to ngrx/store
### Bindings to connect angular/router to ngrx/store


### Setup
Expand All @@ -9,25 +9,20 @@
npm install @ngrx/router-store --save
```

2. Use the `routerReducer` when providing `Store`:
```ts
import { provideStore } from '@ngrx/store';
import { routerReducer } from '@ngrx/router-store';


export const storeProvider = provideStore({
// Your reducers go here,
router: routerReducer
});
```
2. Use the `routerReducer` when providing the `StoreModule.provideStore` and add the `RouterStoreModule.connectRouter` to the `@NgModule.imports`:

3. Install the bindings providers after you setup the router providers:
```ts
import { connectRouterToStore } from '@ngrx/router-store';

bootstrap(App, [
storeProvider,
provideRouter(routes),
connectRouterToStore()
]);
import { StoreModule } from '@ngrx/store';
import { routerReducer, RouterStoreModule } from '@ngrx/router-store';

@NgModule({
imports: [
BrowserModule,
StoreModule.provideStore({ router: routerReducer }),
RouterStoreModule.connectRouter()
],
bootstrap: [ AppComponent ]
})
export class AppModule {
}
```
7 changes: 7 additions & 0 deletions lib/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export const routerActions = {
GO: '[Router] Go',
REPLACE: '[Router] Replace',
SEARCH: '[Router] Search',
SHOW: '[Router] Show',
BACK: '[Router] Back',
FORWARD: '[Router] Forward',
UPDATE_LOCATION: '[Router] Update Location'
Expand All @@ -34,6 +35,12 @@ export function search(query: any): Action {
return { type: routerActions.SEARCH, payload };
}

export function show(path: string, query?: any): Action {
const payload: RouterMethodCall = { path, query };

return { type: routerActions.SHOW, payload };
}

export function back(): Action {
const payload: RouterMethodCall = { };

Expand Down
64 changes: 47 additions & 17 deletions lib/connect.ts
Original file line number Diff line number Diff line change
@@ -1,49 +1,79 @@
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/filter';
import 'rxjs/add/operator/distinctUntilChanged';
import 'rxjs/add/operator/map';
import { Router, LocationChange } from '@ngrx/router';
import { Action, Dispatcher, Store } from '@ngrx/store';
import 'rxjs/add/operator/withLatestFrom';
import '@ngrx/core/add/operator/select';
import { Router, Event, NavigationEnd } from '@angular/router';
import { Location } from '@angular/common';
import { Store, Action } from '@ngrx/store';
import { Observable } from 'rxjs/Observable';
import { Observer } from 'rxjs/Observer';

import { RouterState } from './reducer';
import { RouterMethodCall, routerActions, routerActionTypes } from './actions';

export function projectLocationChanges(state$: Observable<{ router: RouterState }>): Observable<LocationChange> {
return state$.map(s => s.router).filter(change => change !== null);
}

export function listenForRouterMethodActions(router: Router, actions$: Observable<Action>) {
export function listenForRouterMethodActions(router: Router, location: Location, actions$: Observable<Action>) {
actions$
.filter(action => routerActionTypes.indexOf(action.type) > -1)
.subscribe(action => {
const { path, query }: RouterMethodCall = action.payload;
const { path, query: queryParams }: RouterMethodCall = action.payload;
let commands: any[] = [path];

switch (action.type) {
case routerActions.GO:
router.go(path, query);
router.navigate(commands, { queryParams });
break;

case routerActions.REPLACE:
router.replace(path, query);
router.navigate(commands, { queryParams, replaceUrl: true });
break;

case routerActions.SEARCH:
router.search(query);
let url = router.url;
let command = url.split(/\?/)[0];
router.navigate([command], { queryParams });
break;

case routerActions.SHOW:
router.navigate(commands, { queryParams, skipLocationChange: true });
break;

case routerActions.BACK:
router.back();
location.back();
break;

case routerActions.FORWARD:
router.forward();
location.forward();
break;
}
});
}

export function connectRouterActions(router: Observable<LocationChange>, store: Observer<Action>) {
router
.map(change => ({ type: routerActions.UPDATE_LOCATION, payload: change }))
export function selectRouter(store: Store<any>) {
return store.select(state => state.router);
}

export function getLatestUrl(router: Router): Observable<string> {
return router
.events
.filter((event: Event) => event instanceof NavigationEnd)
.map(() => router.url)
.distinctUntilChanged();
}

export function connectRouterActions(router: Router, store: Store<any>) {
getLatestUrl(router)
.withLatestFrom(selectRouter(store))
.filter(([url, rs]) => (rs && rs.path !== url) || !rs)
.map(([path]) => ({ type: routerActions.UPDATE_LOCATION, payload: { path } }))
.subscribe(store);
}

export function listenForStoreChanges(router: Router, store: Store<any>) {
selectRouter(store)
.withLatestFrom(getLatestUrl(router))
.filter(([rs, url]) => rs.path !== url)
.map(([rs]) => rs.path)
.do(url => router.navigateByUrl(url))
.subscribe();
}
14 changes: 4 additions & 10 deletions lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,3 @@
import { Provider } from '@angular/core';

import { LOCATION_CHANGES_PROVIDER } from './location-changes';

export function connectRouterToStore() {
return [
LOCATION_CHANGES_PROVIDER
];
}

export {
go,
replace,
Expand All @@ -21,3 +11,7 @@ export {
routerReducer,
RouterState
} from './reducer';

export {
RouterStoreModule
} from './router-store-module';
17 changes: 0 additions & 17 deletions lib/location-changes.ts

This file was deleted.

5 changes: 3 additions & 2 deletions lib/reducer.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { LocationChange} from '@ngrx/router';
import { Action } from '@ngrx/store';
import { routerActions } from './actions';

export interface RouterState extends LocationChange { }
export interface RouterState {
path: string;
}

export function routerReducer(state: RouterState = null, action: Action): RouterState {
switch (action.type) {
Expand Down
45 changes: 45 additions & 0 deletions lib/router-store-module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { NgModule, APP_BOOTSTRAP_LISTENER, OpaqueToken } from '@angular/core';
import { Location } from '@angular/common';
import { Router } from '@angular/router';
import { Dispatcher, Store, Action } from '@ngrx/store';
import { Observable } from 'rxjs/Observable';

import {
listenForRouterMethodActions,
connectRouterActions,
listenForStoreChanges
} from './connect';

export function setupRouterStore(
router: Router,
location: Location,
dispatcher: Observable<Action>,
store: Store<any>
) {
return () => {
listenForRouterMethodActions(router, location, dispatcher);
connectRouterActions(router, store);
listenForStoreChanges(router, store);
};
}

export function provideRouterConnector() {
return {
provide: APP_BOOTSTRAP_LISTENER,
deps: [ Router, Location, Dispatcher, Store ],
useFactory: setupRouterStore,
multi: true
};
}

@NgModule()
export class RouterStoreModule {
static connectRouter() {
return {
ngModule: RouterStoreModule,
providers: [
provideRouterConnector()
]
};
}
}
27 changes: 14 additions & 13 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,20 +37,21 @@
],
"license": "MIT",
"peerDependencies": {
"rxjs": "5.0.0-beta.6",
"@angular/core": "^2.0.0-rc.1",
"@ngrx/router": "^1.0.0-beta.0",
"rxjs": "^5.0.0-beta.6",
"@angular/common": "^2.0.0-rc.5",
"@angular/core": "^2.0.0-rc.5",
"@angular/router": "^3.0.0-rc.1",
"@ngrx/store": "^1.5.0 || ^2.0.0"
},
"devDependencies": {
"@angular/common": "^2.0.0-rc.1",
"@angular/compiler": "^2.0.0-rc.1",
"@angular/core": "^2.0.0-rc.1",
"@angular/platform-browser": "^2.0.0-rc.1",
"@angular/platform-browser-dynamic": "^2.0.0-rc.1",
"@angular/common": "^2.0.0-rc.5",
"@angular/compiler": "^2.0.0-rc.5",
"@angular/core": "^2.0.0-rc.5",
"@angular/platform-browser": "^2.0.0-rc.5",
"@angular/platform-browser-dynamic": "^2.0.0-rc.5",
"@angular/router": "^3.0.0-rc.1",
"@ngrx/core": "^1.0.0",
"@ngrx/router": "^1.0.0-beta.0",
"@ngrx/store": "^1.5.0",
"@ngrx/store": "^2.0.0",
"conventional-changelog-cli": "^1.1.1",
"core-js": "^2.2.2",
"istanbul-instrumenter-loader": "^0.2.0",
Expand All @@ -65,13 +66,13 @@
"karma-webpack": "^1.7.0",
"npm-run-all": "^1.7.0",
"reflect-metadata": "^0.1.3",
"rxjs": "5.0.0-beta.6",
"rxjs": "^5.0.0-beta.6",
"source-map-loader": "^0.1.5",
"ts-loader": "^0.8.1",
"tslint": "^3.6.0",
"typescript": "^1.8.9",
"typings": "^0.7.12",
"typings": "^1.3.0",
"webpack": "^1.12.14",
"zone.js": "^0.6.8"
"zone.js": "0.6.14"
}
}
10 changes: 10 additions & 0 deletions spec/actions.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,16 @@ describe('Actions', function() {
});
});

it('should provide a "show" action creator', function() {
expect(actions.show('/path', 'query=string')).toEqual({
type: actions.routerActions.SHOW,
payload: {
path: '/path',
query: 'query=string'
}
});
});

it('should provide a "search" action creator', function() {
expect(actions.search('query=string')).toEqual({
type: actions.routerActions.SEARCH,
Expand Down
Loading

0 comments on commit 2af0ed4

Please sign in to comment.