UNPKG

17.4 kBJavaScriptView Raw
1import * as i1 from '@ngrx/store';
2import { createAction, props, isNgrxMockEnvironment, select, ACTIVE_RUNTIME_CHECKS, createFeatureSelector, createSelector } from '@ngrx/store';
3import * as i0 from '@angular/core';
4import { InjectionToken, isDevMode, NgModule, Inject } from '@angular/core';
5import * as i2 from '@angular/router';
6import { NavigationStart, RoutesRecognized, NavigationCancel, NavigationError, NavigationEnd } from '@angular/router';
7import { withLatestFrom } from 'rxjs/operators';
8
9/**
10 * An action dispatched when a router navigation request is fired.
11 */
12const ROUTER_REQUEST = '@ngrx/router-store/request';
13const routerRequestAction = createAction(ROUTER_REQUEST, props());
14/**
15 * An action dispatched when the router navigates.
16 */
17const ROUTER_NAVIGATION = '@ngrx/router-store/navigation';
18const routerNavigationAction = createAction(ROUTER_NAVIGATION, props());
19/**
20 * An action dispatched when the router cancels navigation.
21 */
22const ROUTER_CANCEL = '@ngrx/router-store/cancel';
23const routerCancelAction = createAction(ROUTER_CANCEL, props());
24/**
25 * An action dispatched when the router errors.
26 */
27const ROUTER_ERROR = '@ngrx/router-store/error';
28const routerErrorAction = createAction(ROUTER_ERROR, props());
29/**
30 * An action dispatched after navigation has ended and new route is active.
31 */
32const ROUTER_NAVIGATED = '@ngrx/router-store/navigated';
33const routerNavigatedAction = createAction(ROUTER_NAVIGATED, props());
34
35function routerReducer(state, action) {
36 // Allow compilation with strictFunctionTypes - ref: #1344
37 const routerAction = action;
38 switch (routerAction.type) {
39 case ROUTER_NAVIGATION:
40 case ROUTER_ERROR:
41 case ROUTER_CANCEL:
42 return {
43 state: routerAction.payload.routerState,
44 navigationId: routerAction.payload.event.id,
45 };
46 default:
47 return state;
48 }
49}
50
51class RouterStateSerializer {
52}
53
54class DefaultRouterStateSerializer {
55 serialize(routerState) {
56 return {
57 root: this.serializeRoute(routerState.root),
58 url: routerState.url,
59 };
60 }
61 serializeRoute(route) {
62 const children = route.children.map((c) => this.serializeRoute(c));
63 return {
64 params: route.params,
65 paramMap: route.paramMap,
66 data: route.data,
67 url: route.url,
68 outlet: route.outlet,
69 routeConfig: route.routeConfig
70 ? {
71 component: route.routeConfig.component,
72 path: route.routeConfig.path,
73 pathMatch: route.routeConfig.pathMatch,
74 redirectTo: route.routeConfig.redirectTo,
75 outlet: route.routeConfig.outlet,
76 }
77 : null,
78 queryParams: route.queryParams,
79 queryParamMap: route.queryParamMap,
80 fragment: route.fragment,
81 component: (route.routeConfig
82 ? route.routeConfig.component
83 : undefined),
84 root: undefined,
85 parent: undefined,
86 firstChild: children[0],
87 pathFromRoot: undefined,
88 children,
89 };
90 }
91}
92
93class MinimalRouterStateSerializer {
94 serialize(routerState) {
95 return {
96 root: this.serializeRoute(routerState.root),
97 url: routerState.url,
98 };
99 }
100 serializeRoute(route) {
101 const children = route.children.map((c) => this.serializeRoute(c));
102 return {
103 params: route.params,
104 data: route.data,
105 url: route.url,
106 outlet: route.outlet,
107 routeConfig: route.routeConfig
108 ? {
109 path: route.routeConfig.path,
110 pathMatch: route.routeConfig.pathMatch,
111 redirectTo: route.routeConfig.redirectTo,
112 outlet: route.routeConfig.outlet,
113 }
114 : null,
115 queryParams: route.queryParams,
116 fragment: route.fragment,
117 firstChild: children[0],
118 children,
119 };
120 }
121}
122
123var NavigationActionTiming;
124(function (NavigationActionTiming) {
125 NavigationActionTiming[NavigationActionTiming["PreActivation"] = 1] = "PreActivation";
126 NavigationActionTiming[NavigationActionTiming["PostActivation"] = 2] = "PostActivation";
127})(NavigationActionTiming || (NavigationActionTiming = {}));
128const _ROUTER_CONFIG = new InjectionToken('@ngrx/router-store Internal Configuration');
129const ROUTER_CONFIG = new InjectionToken('@ngrx/router-store Configuration');
130const DEFAULT_ROUTER_FEATURENAME = 'router';
131function _createRouterConfig(config) {
132 return Object.assign({ stateKey: DEFAULT_ROUTER_FEATURENAME, serializer: MinimalRouterStateSerializer, navigationActionTiming: NavigationActionTiming.PreActivation }, config);
133}
134var RouterTrigger;
135(function (RouterTrigger) {
136 RouterTrigger[RouterTrigger["NONE"] = 1] = "NONE";
137 RouterTrigger[RouterTrigger["ROUTER"] = 2] = "ROUTER";
138 RouterTrigger[RouterTrigger["STORE"] = 3] = "STORE";
139})(RouterTrigger || (RouterTrigger = {}));
140/**
141 * Connects RouterModule with StoreModule.
142 *
143 * During the navigation, before any guards or resolvers run, the router will dispatch
144 * a ROUTER_NAVIGATION action, which has the following signature:
145 *
146 * ```
147 * export type RouterNavigationPayload = {
148 * routerState: SerializedRouterStateSnapshot,
149 * event: RoutesRecognized
150 * }
151 * ```
152 *
153 * Either a reducer or an effect can be invoked in response to this action.
154 * If the invoked reducer throws, the navigation will be canceled.
155 *
156 * If navigation gets canceled because of a guard, a ROUTER_CANCEL action will be
157 * dispatched. If navigation results in an error, a ROUTER_ERROR action will be dispatched.
158 *
159 * Both ROUTER_CANCEL and ROUTER_ERROR contain the store state before the navigation
160 * which can be used to restore the consistency of the store.
161 *
162 * Usage:
163 *
164 * ```typescript
165 * @NgModule({
166 * declarations: [AppCmp, SimpleCmp],
167 * imports: [
168 * BrowserModule,
169 * StoreModule.forRoot(mapOfReducers),
170 * RouterModule.forRoot([
171 * { path: '', component: SimpleCmp },
172 * { path: 'next', component: SimpleCmp }
173 * ]),
174 * StoreRouterConnectingModule.forRoot()
175 * ],
176 * bootstrap: [AppCmp]
177 * })
178 * export class AppModule {
179 * }
180 * ```
181 */
182class StoreRouterConnectingModule {
183 constructor(store, router, serializer, errorHandler, config, activeRuntimeChecks) {
184 this.store = store;
185 this.router = router;
186 this.serializer = serializer;
187 this.errorHandler = errorHandler;
188 this.config = config;
189 this.activeRuntimeChecks = activeRuntimeChecks;
190 this.lastEvent = null;
191 this.routerState = null;
192 this.trigger = RouterTrigger.NONE;
193 this.stateKey = this.config.stateKey;
194 if (!isNgrxMockEnvironment() &&
195 isDevMode() &&
196 ((activeRuntimeChecks === null || activeRuntimeChecks === void 0 ? void 0 : activeRuntimeChecks.strictActionSerializability) ||
197 (activeRuntimeChecks === null || activeRuntimeChecks === void 0 ? void 0 : activeRuntimeChecks.strictStateSerializability)) &&
198 this.serializer instanceof DefaultRouterStateSerializer) {
199 console.warn('@ngrx/router-store: The serializability runtime checks cannot be enabled ' +
200 'with the DefaultRouterStateSerializer. The default serializer ' +
201 'has an unserializable router state and actions that are not serializable. ' +
202 'To use the serializability runtime checks either use ' +
203 'the MinimalRouterStateSerializer or implement a custom router state serializer. ' +
204 'This also applies to Ivy with immutability runtime checks.');
205 }
206 this.setUpStoreStateListener();
207 this.setUpRouterEventsListener();
208 }
209 static forRoot(config = {}) {
210 return {
211 ngModule: StoreRouterConnectingModule,
212 providers: [
213 { provide: _ROUTER_CONFIG, useValue: config },
214 {
215 provide: ROUTER_CONFIG,
216 useFactory: _createRouterConfig,
217 deps: [_ROUTER_CONFIG],
218 },
219 {
220 provide: RouterStateSerializer,
221 useClass: config.serializer
222 ? config.serializer
223 : config.routerState === 0 /* Full */
224 ? DefaultRouterStateSerializer
225 : MinimalRouterStateSerializer,
226 },
227 ],
228 };
229 }
230 setUpStoreStateListener() {
231 this.store
232 .pipe(select(this.stateKey), withLatestFrom(this.store))
233 .subscribe(([routerStoreState, storeState]) => {
234 this.navigateIfNeeded(routerStoreState, storeState);
235 });
236 }
237 navigateIfNeeded(routerStoreState, storeState) {
238 if (!routerStoreState || !routerStoreState.state) {
239 return;
240 }
241 if (this.trigger === RouterTrigger.ROUTER) {
242 return;
243 }
244 if (this.lastEvent instanceof NavigationStart) {
245 return;
246 }
247 const url = routerStoreState.state.url;
248 if (!isSameUrl(this.router.url, url)) {
249 this.storeState = storeState;
250 this.trigger = RouterTrigger.STORE;
251 this.router.navigateByUrl(url).catch((error) => {
252 this.errorHandler.handleError(error);
253 });
254 }
255 }
256 setUpRouterEventsListener() {
257 const dispatchNavLate = this.config.navigationActionTiming ===
258 NavigationActionTiming.PostActivation;
259 let routesRecognized;
260 this.router.events
261 .pipe(withLatestFrom(this.store))
262 .subscribe(([event, storeState]) => {
263 this.lastEvent = event;
264 if (event instanceof NavigationStart) {
265 this.routerState = this.serializer.serialize(this.router.routerState.snapshot);
266 if (this.trigger !== RouterTrigger.STORE) {
267 this.storeState = storeState;
268 this.dispatchRouterRequest(event);
269 }
270 }
271 else if (event instanceof RoutesRecognized) {
272 routesRecognized = event;
273 if (!dispatchNavLate && this.trigger !== RouterTrigger.STORE) {
274 this.dispatchRouterNavigation(event);
275 }
276 }
277 else if (event instanceof NavigationCancel) {
278 this.dispatchRouterCancel(event);
279 this.reset();
280 }
281 else if (event instanceof NavigationError) {
282 this.dispatchRouterError(event);
283 this.reset();
284 }
285 else if (event instanceof NavigationEnd) {
286 if (this.trigger !== RouterTrigger.STORE) {
287 if (dispatchNavLate) {
288 this.dispatchRouterNavigation(routesRecognized);
289 }
290 this.dispatchRouterNavigated(event);
291 }
292 this.reset();
293 }
294 });
295 }
296 dispatchRouterRequest(event) {
297 this.dispatchRouterAction(ROUTER_REQUEST, { event });
298 }
299 dispatchRouterNavigation(lastRoutesRecognized) {
300 const nextRouterState = this.serializer.serialize(lastRoutesRecognized.state);
301 this.dispatchRouterAction(ROUTER_NAVIGATION, {
302 routerState: nextRouterState,
303 event: new RoutesRecognized(lastRoutesRecognized.id, lastRoutesRecognized.url, lastRoutesRecognized.urlAfterRedirects, nextRouterState),
304 });
305 }
306 dispatchRouterCancel(event) {
307 this.dispatchRouterAction(ROUTER_CANCEL, {
308 storeState: this.storeState,
309 event,
310 });
311 }
312 dispatchRouterError(event) {
313 this.dispatchRouterAction(ROUTER_ERROR, {
314 storeState: this.storeState,
315 event: new NavigationError(event.id, event.url, `${event}`),
316 });
317 }
318 dispatchRouterNavigated(event) {
319 const routerState = this.serializer.serialize(this.router.routerState.snapshot);
320 this.dispatchRouterAction(ROUTER_NAVIGATED, { event, routerState });
321 }
322 dispatchRouterAction(type, payload) {
323 this.trigger = RouterTrigger.ROUTER;
324 try {
325 this.store.dispatch({
326 type,
327 payload: Object.assign(Object.assign({ routerState: this.routerState }, payload), { event: this.config.routerState === 0 /* Full */
328 ? payload.event
329 : {
330 id: payload.event.id,
331 url: payload.event.url,
332 // safe, as it will just be `undefined` for non-NavigationEnd router events
333 urlAfterRedirects: payload.event
334 .urlAfterRedirects,
335 } }),
336 });
337 }
338 finally {
339 this.trigger = RouterTrigger.NONE;
340 }
341 }
342 reset() {
343 this.trigger = RouterTrigger.NONE;
344 this.storeState = null;
345 this.routerState = null;
346 }
347}
348/** @nocollapse */ /** @nocollapse */ StoreRouterConnectingModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.0.0", ngImport: i0, type: StoreRouterConnectingModule, deps: [{ token: i1.Store }, { token: i2.Router }, { token: RouterStateSerializer }, { token: i0.ErrorHandler }, { token: ROUTER_CONFIG }, { token: ACTIVE_RUNTIME_CHECKS }], target: i0.ɵɵFactoryTarget.NgModule });
349/** @nocollapse */ /** @nocollapse */ StoreRouterConnectingModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "13.0.0", ngImport: i0, type: StoreRouterConnectingModule });
350/** @nocollapse */ /** @nocollapse */ StoreRouterConnectingModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "13.0.0", ngImport: i0, type: StoreRouterConnectingModule });
351i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.0.0", ngImport: i0, type: StoreRouterConnectingModule, decorators: [{
352 type: NgModule,
353 args: [{}]
354 }], ctorParameters: function () {
355 return [{ type: i1.Store }, { type: i2.Router }, { type: RouterStateSerializer }, { type: i0.ErrorHandler }, { type: undefined, decorators: [{
356 type: Inject,
357 args: [ROUTER_CONFIG]
358 }] }, { type: undefined, decorators: [{
359 type: Inject,
360 args: [ACTIVE_RUNTIME_CHECKS]
361 }] }];
362 } });
363/**
364 * Check if the URLs are matching. Accounts for the possibility of trailing "/" in url.
365 */
366function isSameUrl(first, second) {
367 return stripTrailingSlash(first) === stripTrailingSlash(second);
368}
369function stripTrailingSlash(text) {
370 if ((text === null || text === void 0 ? void 0 : text.length) > 0 && text[text.length - 1] === '/') {
371 return text.substring(0, text.length - 1);
372 }
373 return text;
374}
375
376function createRouterSelector() {
377 return createFeatureSelector(DEFAULT_ROUTER_FEATURENAME);
378}
379function getSelectors(selectState = createRouterSelector()) {
380 const selectRouterState = createSelector(selectState, (router) => router && router.state);
381 const selectRootRoute = createSelector(selectRouterState, (routerState) => routerState && routerState.root);
382 const selectCurrentRoute = createSelector(selectRootRoute, (rootRoute) => {
383 if (!rootRoute) {
384 return undefined;
385 }
386 let route = rootRoute;
387 while (route.firstChild) {
388 route = route.firstChild;
389 }
390 return route;
391 });
392 const selectFragment = createSelector(selectRootRoute, (route) => route && route.fragment);
393 const selectQueryParams = createSelector(selectRootRoute, (route) => route && route.queryParams);
394 const selectQueryParam = (param) => createSelector(selectQueryParams, (params) => params && params[param]);
395 const selectRouteParams = createSelector(selectCurrentRoute, (route) => route && route.params);
396 const selectRouteParam = (param) => createSelector(selectRouteParams, (params) => params && params[param]);
397 const selectRouteData = createSelector(selectCurrentRoute, (route) => route && route.data);
398 const selectUrl = createSelector(selectRouterState, (routerState) => routerState && routerState.url);
399 return {
400 selectCurrentRoute,
401 selectFragment,
402 selectQueryParams,
403 selectQueryParam,
404 selectRouteParams,
405 selectRouteParam,
406 selectRouteData,
407 selectUrl,
408 };
409}
410
411/**
412 * DO NOT EDIT
413 *
414 * This file is automatically generated at build
415 */
416
417/**
418 * Generated bundle index. Do not edit.
419 */
420
421export { DEFAULT_ROUTER_FEATURENAME, DefaultRouterStateSerializer, MinimalRouterStateSerializer, NavigationActionTiming, ROUTER_CANCEL, ROUTER_CONFIG, ROUTER_ERROR, ROUTER_NAVIGATED, ROUTER_NAVIGATION, ROUTER_REQUEST, RouterStateSerializer, StoreRouterConnectingModule, createRouterSelector, getSelectors, routerCancelAction, routerErrorAction, routerNavigatedAction, routerNavigationAction, routerReducer, routerRequestAction };