UNPKG

@o3r/routing

Version:

This module helps you to configure an Otter application's routing (POST parameter management, preloading strategy, guard recommendation).

353 lines (342 loc) 18.2 kB
import * as i0 from '@angular/core'; import { Injectable, InjectionToken, NgModule, makeEnvironmentProviders } from '@angular/core'; import * as i1 from '@ngrx/effects'; import { createEffect, ofType } from '@ngrx/effects'; import { ROUTER_REQUEST, ROUTER_NAVIGATED } from '@ngrx/router-store'; import { filter, map, switchMap } from 'rxjs/operators'; import * as i1$1 from '@ngrx/store'; import { createAction, props, on, createReducer, StoreModule, createFeatureSelector, createSelector } from '@ngrx/store'; import { createEntityAdapter } from '@ngrx/entity'; import { APP_BASE_HREF } from '@angular/common'; import { DEFAULT_BUILD_PROPERTIES } from '@o3r/core'; import * as i1$2 from '@angular/router'; import { NavigationEnd } from '@angular/router'; import { of } from 'rxjs'; /** Entity Actions */ const ACTION_REGISTER_ENTITY = '[RoutingGuard] register an entity'; const ACTION_SET_ENTITY_AS_FAILURE = '[RoutingGuard] set an entity state as FAILURE'; const ACTION_SET_ENTITY_AS_SUCCESS_AND_CLEAR_REASON = '[RoutingGuard] set an entity state as SUCCESS and clear reason'; const ACTION_SET_ENTITY_AS_PENDING = '[RoutingGuard] set an entity state as PENDING'; const ACTION_CLEAR_ENTITIES = '[RoutingGuard] clear entities'; const ACTION_CLEAR_FAILURE_REASON = '[RoutingGuard] clear failure reason for an entity'; const ACTION_SET_ENTITY_AS_FAILURE_WITH_REASON = '[RoutingGuard] set an entity state as FAILURE with a reason'; /** * Register a new entity in routing guard store */ const registerRoutingGuardEntity = createAction(ACTION_REGISTER_ENTITY, props()); /** * Set an entity in FAILURE status */ const setRoutingGuardEntityAsFailure = createAction(ACTION_SET_ENTITY_AS_FAILURE, props()); /** * Set an entity in SUCCESS status and clear the reason */ const setRoutingGuardEntityAsSuccessAndClearReason = createAction(ACTION_SET_ENTITY_AS_SUCCESS_AND_CLEAR_REASON, props()); /** * Set an entity in PENDING status */ const setRoutingGuardEntityAsPending = createAction(ACTION_SET_ENTITY_AS_PENDING, props()); /** * Clear only the entities, keeps the other attributes in the state */ const clearRoutingGuardEntities = createAction(ACTION_CLEAR_ENTITIES); /** * Clear only the entities, keeps the other attributes in the state */ const clearRoutingGuardEntitiesFailureReason = createAction(ACTION_CLEAR_FAILURE_REASON, props()); /** * Set entity in FAILURE status with a reason */ const setRoutingGuardEntityFailureWithReason = createAction(ACTION_SET_ENTITY_AS_FAILURE_WITH_REASON, props()); /** * Effect to react on Ngrx router store actions */ class NgrxStoreRouterEffect { constructor(actions$) { this.actions$ = actions$; /** * Clear Router registrations when the active history entry changes (ex : click on back/next button Action) */ this.resetRouterRegistrationOnRequest$ = createEffect(() => this.actions$.pipe(ofType(ROUTER_REQUEST), filter((action) => action.payload.event.navigationTrigger === 'popstate'), map(() => clearRoutingGuardEntities()))); /** * Clear Router registrations when navigation happened */ this.resetRouterRegistrationOnNavigated$ = createEffect(() => this.actions$.pipe(ofType(ROUTER_NAVIGATED), map(() => clearRoutingGuardEntities()))); } /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: NgrxStoreRouterEffect, deps: [{ token: i1.Actions }], target: i0.ɵɵFactoryTarget.Injectable }); } /** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: NgrxStoreRouterEffect }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: NgrxStoreRouterEffect, decorators: [{ type: Injectable }], ctorParameters: () => [{ type: i1.Actions }] }); /** Identifies the status of a registered block */ var RegisteredItemStatus; (function (RegisteredItemStatus) { /** block is ready (async call has not been triggered) */ RegisteredItemStatus["READY"] = "READY"; /** block is waiting for async call to resolve */ RegisteredItemStatus["PENDING"] = "PENDING"; /** block received the async call result and it's a failure */ RegisteredItemStatus["FAILURE"] = "FAILURE"; /** block received the async call result and it's a success */ RegisteredItemStatus["SUCCESS"] = "SUCCESS"; })(RegisteredItemStatus || (RegisteredItemStatus = {})); /** Identifies the failure reason for the registered block */ var RegisteredItemFailureReason; (function (RegisteredItemFailureReason) { /** Routing Failed because the async call result is failure */ RegisteredItemFailureReason["ASYNC_CALL_FAILURE"] = "ASYNC_CALL_FAILURE"; /** Routing failed because the changes to the form has not been saved */ RegisteredItemFailureReason["FORM_UNSAVED"] = "FORM_UNSAVED"; })(RegisteredItemFailureReason || (RegisteredItemFailureReason = {})); /** * Name of the RoutingGuard Store */ const ROUTING_GUARD_STORE_NAME = 'routingGuard'; /** * RoutingGuard Store adapter */ const routingGuardAdapter = createEntityAdapter({ selectId: (model) => model.id }); /** * RoutingGuard Store initial value */ const routingGuardInitialState = routingGuardAdapter.getInitialState({}); /** * List of basic actions for RoutingGuard Store */ const routingGuardReducerFeatures = [ on(registerRoutingGuardEntity, (state, payload) => routingGuardAdapter.addOne({ id: payload.id, status: RegisteredItemStatus.READY }, state)), on(setRoutingGuardEntityAsPending, (state, payload) => routingGuardAdapter.updateOne({ id: payload.id, changes: { status: RegisteredItemStatus.PENDING } }, state)), on(setRoutingGuardEntityAsFailure, (state, payload) => routingGuardAdapter.updateOne({ id: payload.id, changes: { status: RegisteredItemStatus.FAILURE } }, state)), on(setRoutingGuardEntityAsSuccessAndClearReason, (state, payload) => routingGuardAdapter.updateOne({ id: payload.id, changes: { status: RegisteredItemStatus.SUCCESS, blockingReason: undefined } }, state)), on(clearRoutingGuardEntities, (state) => routingGuardAdapter.removeAll(state)), on(clearRoutingGuardEntitiesFailureReason, (state, payload) => routingGuardAdapter.updateOne({ id: payload.id, changes: { status: RegisteredItemStatus.READY, blockingReason: undefined } }, state)), on(setRoutingGuardEntityFailureWithReason, (state, payload) => routingGuardAdapter.updateOne({ id: payload.id, changes: { status: RegisteredItemStatus.FAILURE, blockingReason: payload.reason } }, state)) ]; /** * RoutingGuard Store reducer */ const routingGuardReducer = createReducer(routingGuardInitialState, ...routingGuardReducerFeatures); /** Token of the RoutingGuard reducer */ const ROUTING_GUARD_REDUCER_TOKEN = new InjectionToken('Feature RoutingGuard Reducer'); /** Provide default reducer for RoutingGuard store */ function getDefaultRoutingGuardReducer() { return routingGuardReducer; } class RoutingGuardStoreModule { static forRoot(reducerFactory) { return { ngModule: RoutingGuardStoreModule, providers: [ { provide: ROUTING_GUARD_REDUCER_TOKEN, useFactory: reducerFactory } ] }; } /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: RoutingGuardStoreModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); } /** @nocollapse */ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.10", ngImport: i0, type: RoutingGuardStoreModule, imports: [i1$1.StoreFeatureModule] }); } /** @nocollapse */ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: RoutingGuardStoreModule, providers: [ { provide: ROUTING_GUARD_REDUCER_TOKEN, useFactory: getDefaultRoutingGuardReducer } ], imports: [StoreModule.forFeature(ROUTING_GUARD_STORE_NAME, ROUTING_GUARD_REDUCER_TOKEN)] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: RoutingGuardStoreModule, decorators: [{ type: NgModule, args: [{ imports: [ StoreModule.forFeature(ROUTING_GUARD_STORE_NAME, ROUTING_GUARD_REDUCER_TOKEN) ], providers: [ { provide: ROUTING_GUARD_REDUCER_TOKEN, useFactory: getDefaultRoutingGuardReducer } ] }] }] }); const { selectIds, selectEntities, selectAll, selectTotal } = routingGuardAdapter.getSelectors(); /** Select RoutingGuard State */ const selectRoutingGuardState = createFeatureSelector(ROUTING_GUARD_STORE_NAME); /** Select the array of RoutingGuard ids */ const selectRoutingGuardIds = createSelector(selectRoutingGuardState, selectIds); /** Select the array of RoutingGuard */ const selectAllRoutingGuard = createSelector(selectRoutingGuardState, selectAll); /** Select the dictionary of RoutingGuard entities */ const selectRoutingGuardEntities = createSelector(selectRoutingGuardState, selectEntities); /** Select the total RoutingGuard count */ const selectRoutingGuardTotal = createSelector(selectRoutingGuardState, selectTotal); /** * Selector used to retrieve the list of entities' status */ const selectRoutingGuardEntitiesStatusList = createSelector(selectRoutingGuardState, (state) => { return Object.keys(state.entities) .filter((key) => !!state.entities[key]) .map((key) => state.entities[key].status); }); /** * Selector used to retrieve the list of entities' blocking reasons for FAILURE status. */ const selectRoutingGuardEntitiesBlockingReasons = createSelector(selectRoutingGuardState, (state) => { return Object.values(state.entities) .filter((entity) => !!entity && !!entity.blockingReason) .map((entity) => entity.blockingReason); }); /** * Selector used to check that no item is in PENDING state. * For example, we will rely on this selector to trigger the canDeactivate logic in the RoutingGuard */ const hasNoEntitiesInPendingState = createSelector(selectRoutingGuardEntitiesStatusList, (statusList) => { return !statusList.includes(RegisteredItemStatus.PENDING); }); /** * Selector used to check that no item is in FAILURE state. * For example, we will rely on this selector to authorize or not the navigation in the RoutingGuard */ const hasNoEntitiesInFailureState = createSelector(selectRoutingGuardEntitiesStatusList, (statusList) => { return !statusList.includes(RegisteredItemStatus.FAILURE); }); /** * Selector used to check that no item is in READY or FAILURE state. * For example, we will rely on this selector to trigger the router navigation and display a loader */ const hasNoEntityInReadyOrFailureState = createSelector(selectRoutingGuardEntitiesStatusList, (statusList) => { return !statusList.some((status) => status === RegisteredItemStatus.READY || status === RegisteredItemStatus.FAILURE); }); /** * Selector used to check that no item is in FAILURE state with a blocking reason provided. * Takes `blockingReason: RegisteredItemFailureReason` as aproperty parameter. */ const hasNoEntityFailureStateWithReasons = createSelector(selectRoutingGuardEntitiesBlockingReasons, (reasons, properties) => { return !reasons.includes(properties.blockingReason); }); /** * AppBaseHref factory function * The APP_BASE_HREF will assume one of the following values (ordered by priority): * 1- undefined (or disabled) if it's not a PROD environment * 2- the content of the data tag data-appbasehref in the body if it exists * 3- the content of config.APP_BASE_HREF if defined * 4- otherwise, undefined * @param config The application environment configuration */ function appBaseHrefFactory(config) { if (config.ENVIRONMENT !== 'prod') { return; } const dynamicBaseHref = document.body.dataset.appbasehref; if (typeof dynamicBaseHref !== 'undefined') { return dynamicBaseHref; } return typeof config.APP_BASE_HREF === 'string' ? config.APP_BASE_HREF : undefined; } const ENVIRONMENT_CONFIG_TOKEN = new InjectionToken('Environment config'); /** * @deprecated Will be removed in v14. */ class AppServerRoutingModule { /** * Injects the APP_BASE_HREF with a custom factory * @param config The application environment configuration * @deprecated Please use {@link provideEnvironment} instead. Will be removed in v14. */ static forRoot(config) { return { ngModule: AppServerRoutingModule, providers: [ { provide: ENVIRONMENT_CONFIG_TOKEN, useValue: { ...DEFAULT_BUILD_PROPERTIES, ...config } }, { provide: APP_BASE_HREF, useFactory: appBaseHrefFactory, deps: [ENVIRONMENT_CONFIG_TOKEN] } ] }; } /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AppServerRoutingModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); } /** @nocollapse */ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.10", ngImport: i0, type: AppServerRoutingModule }); } /** @nocollapse */ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AppServerRoutingModule }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AppServerRoutingModule, decorators: [{ type: NgModule }] }); /** * Specify a custom base href * @param baseHref */ function withBaseHref(baseHref) { return { ɵkind: 'base-href', ɵproviders: [ { provide: APP_BASE_HREF, ...(typeof baseHref === 'function' ? { useFactory: baseHref, deps: [ENVIRONMENT_CONFIG_TOKEN] } : { useValue: baseHref }) } ] }; } /** * Provide environment configuration * @note it will also provide APP_BASE_HREF based on the environment configuration * @param config * @param features */ function provideEnvironment(config, ...features) { const additionalProviders = []; const baseHrefFeature = features.find((f) => f.ɵkind === 'base-href') ?? withBaseHref(appBaseHrefFactory); additionalProviders.push(baseHrefFeature.ɵproviders); return makeEnvironmentProviders([ { provide: ENVIRONMENT_CONFIG_TOKEN, useValue: { ...DEFAULT_BUILD_PROPERTIES, ...config } }, additionalProviders ]); } /** * Check if the route has preload instructions * @param data Route data */ function hasPreloadingOnDemand(data) { if (!data) { return false; } return (Array.isArray(data.preloadOn) && data.preloadOn.length > 0) || data.preloadOn === '*' || data.preloadOn instanceof RegExp; } /** * Otter Preloading strategy base on previous route * @inheritDoc */ class O3rOnNavigationPreloadingStrategy { constructor(router) { this.router = router; } /** * Check if the module should be preloaded based on the data preload array of routes or regex value * @param data Route data * @param url URL of current page */ isUrlMatchingPreloadConfig(data, url) { return (Array.isArray(data.preloadOn) && data.preloadOn.includes(url)) || (data.preloadOn instanceof RegExp && data.preloadOn.test(url)); } /** @inheritDoc */ preload(route, preload) { if ((route.path || route.matcher) && hasPreloadingOnDemand(route.data)) { // On application landing page, check the route to preload. if (route.data.preloadOn === '*' || this.isUrlMatchingPreloadConfig(route.data, this.router.routerState.snapshot.url)) { return preload(); } // On route navigation end, load the routes to preload for the current route. return this.router.events .pipe(filter((r) => r instanceof NavigationEnd && this.isUrlMatchingPreloadConfig(route.data, r.url)), switchMap(() => preload())); } return of(null); } /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: O3rOnNavigationPreloadingStrategy, deps: [{ token: i1$2.Router }], target: i0.ɵɵFactoryTarget.Injectable }); } /** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: O3rOnNavigationPreloadingStrategy }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: O3rOnNavigationPreloadingStrategy, decorators: [{ type: Injectable }], ctorParameters: () => [{ type: i1$2.Router }] }); /** * Generated bundle index. Do not edit. */ export { AppServerRoutingModule, ENVIRONMENT_CONFIG_TOKEN, NgrxStoreRouterEffect, O3rOnNavigationPreloadingStrategy, ROUTING_GUARD_REDUCER_TOKEN, ROUTING_GUARD_STORE_NAME, RegisteredItemFailureReason, RegisteredItemStatus, RoutingGuardStoreModule, appBaseHrefFactory, clearRoutingGuardEntities, clearRoutingGuardEntitiesFailureReason, getDefaultRoutingGuardReducer, hasNoEntitiesInFailureState, hasNoEntitiesInPendingState, hasNoEntityFailureStateWithReasons, hasNoEntityInReadyOrFailureState, hasPreloadingOnDemand, provideEnvironment, registerRoutingGuardEntity, routingGuardAdapter, routingGuardInitialState, routingGuardReducer, routingGuardReducerFeatures, selectAllRoutingGuard, selectRoutingGuardEntities, selectRoutingGuardEntitiesBlockingReasons, selectRoutingGuardEntitiesStatusList, selectRoutingGuardIds, selectRoutingGuardState, selectRoutingGuardTotal, setRoutingGuardEntityAsFailure, setRoutingGuardEntityAsPending, setRoutingGuardEntityAsSuccessAndClearReason, setRoutingGuardEntityFailureWithReason, withBaseHref }; //# sourceMappingURL=o3r-routing.mjs.map