To connect the Angular router module with the NgRx store module, import the store module via
StoreRouterConnectingModule.forRoot()
.
interface StoreRouterConfig {
stateKey?: string;
serializer?: new (...args: any[]) => RouterStateSerializer;
navigationActionTiming?: NavigationActionTiming;
}
stateKey
: The name of reducer key, defaults torouter
serializer
: How a router snapshot is serialized. Defaults toDefaultRouterStateSerializer
. See Custom Router State Serializer for more information.navigationActionTiming
: When theROUTER_NAVIGATION
is dispatched. Defaults toNavigationActionTiming.PreActivation
. See Navigation Action Timing for more information.
During each navigation cycle, a RouterNavigationAction
is dispatched with a snapshot of the state in its payload, the RouterStateSnapshot
. The RouterStateSnapshot
is a large complex structure, containing many pieces of information about the current state and what's rendered by the router. This can cause performance
issues when used with the Store Devtools. In most cases, you may only need a piece of information from the RouterStateSnapshot
. In order to pare down the RouterStateSnapshot
provided during navigation, you provide a custom serializer for the snapshot to only return what you need to be added to the payload and store.
Additionally, the router state snapshot is a mutable object, which can cause issues when developing with store freeze to prevent direct state mutations. This can be avoided by using a custom serializer.
Your custom serializer should implement the abstract class RouterStateSerializer
and return a snapshot which should have an interface extending BaseRouterStoreState
.
You then provide the serializer through the config.
import { StoreModule, ActionReducerMap } from '@ngrx/store';
import { Params, RouterStateSnapshot } from '@angular/router';
import {
StoreRouterConnectingModule,
routerReducer,
RouterReducerState,
RouterStateSerializer,
} from '@ngrx/router-store';
export interface RouterStateUrl {
url: string;
params: Params;
queryParams: Params;
}
export interface State {
router: RouterReducerState<RouterStateUrl>;
}
@Injectable()
export class CustomSerializer implements RouterStateSerializer<RouterStateUrl> {
serialize(routerState: RouterStateSnapshot): RouterStateUrl {
let route = routerState.root;
while (route.firstChild) {
route = route.firstChild;
}
const {
url,
root: { queryParams },
} = routerState;
const { params } = route;
// Only return an object including the URL, params and query params
// instead of the entire snapshot
return { url, params, queryParams };
}
}
export const reducers: ActionReducerMap<State> = {
router: routerReducer,
};
@NgModule({
imports: [
StoreModule.forRoot(reducers),
RouterModule.forRoot([
// routes
]),
StoreRouterConnectingModule.forRoot({
serializer: RouterStateSerializer,
}),
],
})
export class AppModule {}
ROUTER_NAVIGATION
is by default dispatched before any guards or resolvers run. This may not always be ideal, for example if you rely on the action to be dispatched after guards and resolvers successfully ran and the new route will be activated. You can change the dispatch timing by providing the correspondig config:
StoreRouterConnectingModule.forRoot({
navigationActionTiming: NavigationActionTiming.PostActivation,
});