@ngxs/router-plugin
Version:
router plugin for @ngxs/store
387 lines (379 loc) • 14.7 kB
JavaScript
import * as i0 from '@angular/core';
import { inject, NgZone, Injectable, NgModule, makeEnvironmentProviders } from '@angular/core';
import * as i1 from '@ngxs/store';
import { StateToken, Store, Action, Selector, State, NgxsModule, provideStates } from '@ngxs/store';
import { ɵNGXS_ROUTER_PLUGIN_OPTIONS as _NGXS_ROUTER_PLUGIN_OPTIONS, ɵUSER_OPTIONS as _USER_OPTIONS, ɵcreateRouterPluginOptions as _createRouterPluginOptions } from '@ngxs/router-plugin/internals';
import { __decorate, __metadata } from 'tslib';
import { Router, NavigationStart, RoutesRecognized, ResolveEnd, NavigationCancel, NavigationError, NavigationEnd } from '@angular/router';
import { ReplaySubject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
/**
* Public event api of the router
*/
class Navigate {
static { this.type = '[Router] Navigate'; }
constructor(path, queryParams, extras) {
this.path = path;
this.queryParams = queryParams;
this.extras = extras;
}
}
/**
*
* Angular Routers internal state events
*
*/
/**
* An action dispatched when the router starts the navigation.
*/
class RouterRequest {
static { this.type = '[Router] RouterRequest'; }
constructor(routerState, event, trigger = 'none') {
this.routerState = routerState;
this.event = event;
this.trigger = trigger;
}
}
/**
* An action dispatched when the router navigates.
*/
class RouterNavigation {
static { this.type = '[Router] RouterNavigation'; }
constructor(routerState, event, trigger = 'none') {
this.routerState = routerState;
this.event = event;
this.trigger = trigger;
}
}
/**
* An action dispatched when the router cancel navigation.
*/
class RouterCancel {
static { this.type = '[Router] RouterCancel'; }
constructor(routerState, storeState, event, trigger = 'none') {
this.routerState = routerState;
this.storeState = storeState;
this.event = event;
this.trigger = trigger;
}
}
/**
* An action dispatched when the router errors.
*/
class RouterError {
static { this.type = '[Router] RouterError'; }
constructor(routerState, storeState, event, trigger = 'none') {
this.routerState = routerState;
this.storeState = storeState;
this.event = event;
this.trigger = trigger;
}
}
/**
* An action dispatched when the `ResolveEnd` event is triggered.
*/
class RouterDataResolved {
static { this.type = '[Router] RouterDataResolved'; }
constructor(routerState, event, trigger = 'none') {
this.routerState = routerState;
this.event = event;
this.trigger = trigger;
}
}
/**
* An action dispatched when the router navigation has been finished successfully.
*/
class RouterNavigated {
static { this.type = '[Router] RouterNavigated'; }
constructor(routerState, event, trigger = 'none') {
this.routerState = routerState;
this.event = event;
this.trigger = trigger;
}
}
class RouterStateSerializer {
}
class DefaultRouterStateSerializer {
serialize(routerState) {
return {
root: this.serializeRoute(routerState.root),
url: routerState.url
};
}
serializeRoute(route) {
const children = route.children.map(c => this.serializeRoute(c));
return {
url: route.url,
title: route.title,
params: route.params,
queryParams: route.queryParams,
fragment: route.fragment,
data: route.data,
outlet: route.outlet,
component: null,
routeConfig: null,
root: null,
parent: null,
firstChild: children[0],
children: children,
pathFromRoot: null,
paramMap: route.paramMap,
queryParamMap: route.queryParamMap,
toString: route.toString
};
}
}
// NGXS doesn't permit untyped selectors, such as `select(RouterState)`,
// as the `RouterState` class itself lacks type information. Therefore,
// the following state token must replace `RouterState`.
const ROUTER_STATE_TOKEN = new StateToken('router');
let RouterState = class RouterState {
static state(state) {
// The `state` is optional if the selector is invoked before the router
// state is registered in NGXS.
return state?.state;
}
static url(state) {
return state?.state?.url;
}
constructor() {
this._store = inject(Store);
this._router = inject(Router);
this._serializer = inject(RouterStateSerializer);
this._ngZone = inject(NgZone);
/**
* Determines how navigation was performed by the `RouterState` itself
* or outside via `new Navigate(...)`
*/
this._trigger = 'none';
/**
* That's the serialized state from the `Router` class
*/
this._routerState = null;
/**
* That's the value of the `RouterState` state
*/
this._storeState = null;
this._lastEvent = null;
this._options = inject(_NGXS_ROUTER_PLUGIN_OPTIONS);
this._destroy$ = new ReplaySubject(1);
this._setUpStoreListener();
this._setUpRouterEventsListener();
}
ngOnDestroy() {
this._destroy$.next();
}
navigate(_, action) {
return this._ngZone.run(() => this._router.navigate(action.path, {
queryParams: action.queryParams,
...action.extras
}));
}
angularRouterAction(ctx, action) {
ctx.setState({
trigger: action.trigger,
state: action.routerState,
navigationId: action.event.id
});
}
_setUpStoreListener() {
const routerState$ = this._store
.select(ROUTER_STATE_TOKEN)
.pipe(takeUntil(this._destroy$));
routerState$.subscribe((state) => {
this._navigateIfNeeded(state);
});
}
_navigateIfNeeded(routerState) {
if (routerState && routerState.trigger === 'devtools') {
this._storeState = this._store.selectSnapshot(ROUTER_STATE_TOKEN);
}
const canSkipNavigation = !this._storeState ||
!this._storeState.state ||
!routerState ||
routerState.trigger === 'router' ||
this._router.url === this._storeState.state.url ||
this._lastEvent instanceof NavigationStart;
if (canSkipNavigation) {
return;
}
this._storeState = this._store.selectSnapshot(ROUTER_STATE_TOKEN);
this._trigger = 'store';
this._ngZone.run(() => this._router.navigateByUrl(this._storeState.state.url));
}
_setUpRouterEventsListener() {
const dispatchRouterNavigationLate = this._options != null &&
this._options.navigationActionTiming === 2 /* NavigationActionTiming.PostActivation */;
let lastRoutesRecognized;
const events$ = this._router.events.pipe(takeUntil(this._destroy$));
events$.subscribe(event => {
this._lastEvent = event;
if (event instanceof NavigationStart) {
this._navigationStart(event);
}
else if (event instanceof RoutesRecognized) {
lastRoutesRecognized = event;
if (!dispatchRouterNavigationLate && this._trigger !== 'store') {
this._dispatchRouterNavigation(lastRoutesRecognized);
}
}
else if (event instanceof ResolveEnd) {
this._dispatchRouterDataResolved(event);
}
else if (event instanceof NavigationCancel) {
this._dispatchRouterCancel(event);
this._reset();
}
else if (event instanceof NavigationError) {
this._dispatchRouterError(event);
this._reset();
}
else if (event instanceof NavigationEnd) {
if (this._trigger !== 'store') {
if (dispatchRouterNavigationLate) {
this._dispatchRouterNavigation(lastRoutesRecognized);
}
this._dispatchRouterNavigated(event);
}
this._reset();
}
});
}
/** Reacts to `NavigationStart`. */
_navigationStart(event) {
this._routerState = this._serializer.serialize(this._router.routerState.snapshot);
if (this._trigger !== 'none') {
this._storeState = this._store.selectSnapshot(ROUTER_STATE_TOKEN);
this._dispatchRouterAction(new RouterRequest(this._routerState, event, this._trigger));
}
}
/** Reacts to `ResolveEnd`. */
_dispatchRouterDataResolved(event) {
const routerState = this._serializer.serialize(event.state);
this._dispatchRouterAction(new RouterDataResolved(routerState, event, this._trigger));
}
/** Reacts to `RoutesRecognized` or `NavigationEnd`, depends on the `navigationActionTiming`. */
_dispatchRouterNavigation(lastRoutesRecognized) {
const nextRouterState = this._serializer.serialize(lastRoutesRecognized.state);
this._dispatchRouterAction(new RouterNavigation(nextRouterState, new RoutesRecognized(lastRoutesRecognized.id, lastRoutesRecognized.url, lastRoutesRecognized.urlAfterRedirects, nextRouterState), this._trigger));
}
/** Reacts to `NavigationCancel`. */
_dispatchRouterCancel(event) {
this._dispatchRouterAction(new RouterCancel(this._routerState, this._storeState, event, this._trigger));
}
/** Reacts to `NavigationEnd`. */
_dispatchRouterError(event) {
this._dispatchRouterAction(new RouterError(this._routerState, this._storeState, new NavigationError(event.id, event.url, `${event}`), this._trigger));
}
/** Reacts to `NavigationEnd`. */
_dispatchRouterNavigated(event) {
const routerState = this._serializer.serialize(this._router.routerState.snapshot);
this._dispatchRouterAction(new RouterNavigated(routerState, event, this._trigger));
}
_dispatchRouterAction(action) {
this._trigger = 'router';
try {
this._store.dispatch(action);
}
finally {
this._trigger = 'none';
}
}
_reset() {
this._trigger = 'none';
this._storeState = null;
this._routerState = null;
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.0", ngImport: i0, type: RouterState, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.0.0", ngImport: i0, type: RouterState }); }
};
__decorate([
Action(Navigate),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Navigate]),
__metadata("design:returntype", void 0)
], RouterState.prototype, "navigate", null);
__decorate([
Action([
(RouterRequest),
(RouterNavigation),
(RouterError),
(RouterCancel),
(RouterDataResolved),
(RouterNavigated)
]),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Object]),
__metadata("design:returntype", void 0)
], RouterState.prototype, "angularRouterAction", null);
__decorate([
Selector(),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object]),
__metadata("design:returntype", void 0)
], RouterState, "state", null);
__decorate([
Selector(),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object]),
__metadata("design:returntype", Object)
], RouterState, "url", null);
RouterState = __decorate([
State({
name: ROUTER_STATE_TOKEN,
defaults: {
state: undefined,
navigationId: undefined,
trigger: 'none'
}
}),
__metadata("design:paramtypes", [])
], RouterState);
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.0", ngImport: i0, type: RouterState, decorators: [{
type: Injectable
}], ctorParameters: () => [], propDecorators: { navigate: [], angularRouterAction: [] } });
class NgxsRouterPluginModule {
static forRoot(options) {
return {
ngModule: NgxsRouterPluginModule,
providers: [
{ provide: _USER_OPTIONS, useValue: options },
{
provide: _NGXS_ROUTER_PLUGIN_OPTIONS,
useFactory: _createRouterPluginOptions,
deps: [_USER_OPTIONS]
},
{ provide: RouterStateSerializer, useClass: DefaultRouterStateSerializer }
]
};
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.0", ngImport: i0, type: NgxsRouterPluginModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
/** @nocollapse */ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.0.0", ngImport: i0, type: NgxsRouterPluginModule, imports: [i1.ɵNgxsFeatureModule] }); }
/** @nocollapse */ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.0.0", ngImport: i0, type: NgxsRouterPluginModule, imports: [NgxsModule.forFeature([RouterState])] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.0.0", ngImport: i0, type: NgxsRouterPluginModule, decorators: [{
type: NgModule,
args: [{
imports: [NgxsModule.forFeature([RouterState])]
}]
}] });
function withNgxsRouterPlugin(options) {
return makeEnvironmentProviders([
provideStates([RouterState]),
{ provide: _USER_OPTIONS, useValue: options },
{
provide: _NGXS_ROUTER_PLUGIN_OPTIONS,
useFactory: _createRouterPluginOptions,
deps: [_USER_OPTIONS]
},
{ provide: RouterStateSerializer, useClass: DefaultRouterStateSerializer }
]);
}
/**
* The public api for consumers of @ngxs/router-plugin
*/
/**
* Generated bundle index. Do not edit.
*/
export { DefaultRouterStateSerializer, Navigate, NgxsRouterPluginModule, ROUTER_STATE_TOKEN, RouterCancel, RouterDataResolved, RouterError, RouterNavigated, RouterNavigation, RouterRequest, RouterState, RouterStateSerializer, withNgxsRouterPlugin };
//# sourceMappingURL=ngxs-router-plugin.mjs.map