refactor(router): refactor and simplify router RxJS chains (#40290)
Refactor and simplifiy RxJS usage in the router package in order to reduce its size and increase performance. PR Close #40290
This commit is contained in:
parent
4a7a649852
commit
9105005192
|
@ -66,4 +66,4 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1238,12 +1238,6 @@
|
||||||
{
|
{
|
||||||
"name": "findPath"
|
"name": "findPath"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "fireActivationStart"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "fireChildActivationStart"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "first"
|
"name": "first"
|
||||||
},
|
},
|
||||||
|
@ -1832,12 +1826,6 @@
|
||||||
{
|
{
|
||||||
"name": "routerNgProbeToken"
|
"name": "routerNgProbeToken"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "runCanActivate"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "runCanActivateChild"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "rxSubscriber"
|
"name": "rxSubscriber"
|
||||||
},
|
},
|
||||||
|
|
|
@ -105,12 +105,10 @@ export class RouterLinkActive implements OnChanges, OnDestroy, AfterContentInit
|
||||||
/** @nodoc */
|
/** @nodoc */
|
||||||
ngAfterContentInit(): void {
|
ngAfterContentInit(): void {
|
||||||
// `of(null)` is used to force subscribe body to execute once immediately (like `startWith`).
|
// `of(null)` is used to force subscribe body to execute once immediately (like `startWith`).
|
||||||
from([this.links.changes, this.linksWithHrefs.changes, of(null)])
|
of(this.links.changes, this.linksWithHrefs.changes, of(null)).pipe(mergeAll()).subscribe(_ => {
|
||||||
.pipe(mergeAll())
|
this.update();
|
||||||
.subscribe(_ => {
|
this.subscribeToEachLinkOnChanges();
|
||||||
this.update();
|
});
|
||||||
this.subscribeToEachLinkOnChanges();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private subscribeToEachLinkOnChanges() {
|
private subscribeToEachLinkOnChanges() {
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Injector} from '@angular/core';
|
import {Injector} from '@angular/core';
|
||||||
import {MonoTypeOperatorFunction, Observable} from 'rxjs';
|
import {MonoTypeOperatorFunction} from 'rxjs';
|
||||||
import {map, switchMap} from 'rxjs/operators';
|
import {map, switchMap} from 'rxjs/operators';
|
||||||
|
|
||||||
import {applyRedirects as applyRedirectsFn} from '../apply_redirects';
|
import {applyRedirects as applyRedirectsFn} from '../apply_redirects';
|
||||||
|
@ -19,9 +19,7 @@ import {UrlSerializer} from '../url_tree';
|
||||||
export function applyRedirects(
|
export function applyRedirects(
|
||||||
moduleInjector: Injector, configLoader: RouterConfigLoader, urlSerializer: UrlSerializer,
|
moduleInjector: Injector, configLoader: RouterConfigLoader, urlSerializer: UrlSerializer,
|
||||||
config: Routes): MonoTypeOperatorFunction<NavigationTransition> {
|
config: Routes): MonoTypeOperatorFunction<NavigationTransition> {
|
||||||
return function(source: Observable<NavigationTransition>) {
|
return switchMap(
|
||||||
return source.pipe(switchMap(
|
t => applyRedirectsFn(moduleInjector, configLoader, urlSerializer, t.extractedUrl, config)
|
||||||
t => applyRedirectsFn(moduleInjector, configLoader, urlSerializer, t.extractedUrl, config)
|
.pipe(map(urlAfterRedirects => ({...t, urlAfterRedirects}))));
|
||||||
.pipe(map(urlAfterRedirects => ({...t, urlAfterRedirects})))));
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,8 +7,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Injector} from '@angular/core';
|
import {Injector} from '@angular/core';
|
||||||
import {defer, from, MonoTypeOperatorFunction, Observable, of} from 'rxjs';
|
import {concat, defer, from, MonoTypeOperatorFunction, Observable, of} from 'rxjs';
|
||||||
import {concatAll, concatMap, first, map, mergeMap} from 'rxjs/operators';
|
import {concatMap, first, map, mergeMap} from 'rxjs/operators';
|
||||||
|
|
||||||
import {ActivationStart, ChildActivationStart, Event} from '../events';
|
import {ActivationStart, ChildActivationStart, Event} from '../events';
|
||||||
import {CanActivateChildFn, CanActivateFn, CanDeactivateFn} from '../interfaces';
|
import {CanActivateChildFn, CanActivateFn, CanDeactivateFn} from '../interfaces';
|
||||||
|
@ -23,25 +23,23 @@ import {prioritizedGuardValue} from './prioritized_guard_value';
|
||||||
|
|
||||||
export function checkGuards(moduleInjector: Injector, forwardEvent?: (evt: Event) => void):
|
export function checkGuards(moduleInjector: Injector, forwardEvent?: (evt: Event) => void):
|
||||||
MonoTypeOperatorFunction<NavigationTransition> {
|
MonoTypeOperatorFunction<NavigationTransition> {
|
||||||
return function(source: Observable<NavigationTransition>) {
|
return mergeMap(t => {
|
||||||
return source.pipe(mergeMap(t => {
|
const {targetSnapshot, currentSnapshot, guards: {canActivateChecks, canDeactivateChecks}} = t;
|
||||||
const {targetSnapshot, currentSnapshot, guards: {canActivateChecks, canDeactivateChecks}} = t;
|
if (canDeactivateChecks.length === 0 && canActivateChecks.length === 0) {
|
||||||
if (canDeactivateChecks.length === 0 && canActivateChecks.length === 0) {
|
return of({...t, guardsResult: true});
|
||||||
return of({...t, guardsResult: true});
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return runCanDeactivateChecks(
|
return runCanDeactivateChecks(
|
||||||
canDeactivateChecks, targetSnapshot!, currentSnapshot, moduleInjector)
|
canDeactivateChecks, targetSnapshot!, currentSnapshot, moduleInjector)
|
||||||
.pipe(
|
.pipe(
|
||||||
mergeMap(canDeactivate => {
|
mergeMap(canDeactivate => {
|
||||||
return canDeactivate && isBoolean(canDeactivate) ?
|
return canDeactivate && isBoolean(canDeactivate) ?
|
||||||
runCanActivateChecks(
|
runCanActivateChecks(
|
||||||
targetSnapshot!, canActivateChecks, moduleInjector, forwardEvent) :
|
targetSnapshot!, canActivateChecks, moduleInjector, forwardEvent) :
|
||||||
of(canDeactivate);
|
of(canDeactivate);
|
||||||
}),
|
}),
|
||||||
map(guardsResult => ({...t, guardsResult})));
|
map(guardsResult => ({...t, guardsResult})));
|
||||||
}));
|
});
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function runCanDeactivateChecks(
|
function runCanDeactivateChecks(
|
||||||
|
@ -61,15 +59,11 @@ function runCanActivateChecks(
|
||||||
forwardEvent?: (evt: Event) => void) {
|
forwardEvent?: (evt: Event) => void) {
|
||||||
return from(checks).pipe(
|
return from(checks).pipe(
|
||||||
concatMap((check: CanActivate) => {
|
concatMap((check: CanActivate) => {
|
||||||
return from([
|
return concat(
|
||||||
fireChildActivationStart(check.route.parent, forwardEvent),
|
fireChildActivationStart(check.route.parent, forwardEvent),
|
||||||
fireActivationStart(check.route, forwardEvent),
|
fireActivationStart(check.route, forwardEvent),
|
||||||
runCanActivateChild(futureSnapshot, check.path, moduleInjector),
|
runCanActivateChild(futureSnapshot, check.path, moduleInjector),
|
||||||
runCanActivate(futureSnapshot, check.route, moduleInjector)
|
runCanActivate(futureSnapshot, check.route, moduleInjector));
|
||||||
])
|
|
||||||
.pipe(concatAll(), first(result => {
|
|
||||||
return result !== true;
|
|
||||||
}, true as boolean | UrlTree));
|
|
||||||
}),
|
}),
|
||||||
first(result => {
|
first(result => {
|
||||||
return result !== true;
|
return result !== true;
|
||||||
|
|
|
@ -18,8 +18,7 @@ declare type INTERIM_VALUES = typeof INITIAL_VALUE | boolean | UrlTree;
|
||||||
export function prioritizedGuardValue():
|
export function prioritizedGuardValue():
|
||||||
OperatorFunction<Observable<boolean|UrlTree>[], boolean|UrlTree> {
|
OperatorFunction<Observable<boolean|UrlTree>[], boolean|UrlTree> {
|
||||||
return switchMap(obs => {
|
return switchMap(obs => {
|
||||||
return combineLatest(
|
return combineLatest(obs.map(o => o.pipe(take(1), startWith(INITIAL_VALUE as INTERIM_VALUES))))
|
||||||
...obs.map(o => o.pipe(take(1), startWith(INITIAL_VALUE as INTERIM_VALUES))))
|
|
||||||
.pipe(
|
.pipe(
|
||||||
scan(
|
scan(
|
||||||
(acc: INTERIM_VALUES, list: INTERIM_VALUES[]) => {
|
(acc: INTERIM_VALUES, list: INTERIM_VALUES[]) => {
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Type} from '@angular/core';
|
import {Type} from '@angular/core';
|
||||||
import {MonoTypeOperatorFunction, Observable} from 'rxjs';
|
import {MonoTypeOperatorFunction} from 'rxjs';
|
||||||
import {map, mergeMap} from 'rxjs/operators';
|
import {map, mergeMap} from 'rxjs/operators';
|
||||||
|
|
||||||
import {Route} from '../config';
|
import {Route} from '../config';
|
||||||
|
@ -19,11 +19,9 @@ export function recognize(
|
||||||
rootComponentType: Type<any>|null, config: Route[], serializer: (url: UrlTree) => string,
|
rootComponentType: Type<any>|null, config: Route[], serializer: (url: UrlTree) => string,
|
||||||
paramsInheritanceStrategy: 'emptyOnly'|'always',
|
paramsInheritanceStrategy: 'emptyOnly'|'always',
|
||||||
relativeLinkResolution: 'legacy'|'corrected'): MonoTypeOperatorFunction<NavigationTransition> {
|
relativeLinkResolution: 'legacy'|'corrected'): MonoTypeOperatorFunction<NavigationTransition> {
|
||||||
return function(source: Observable<NavigationTransition>) {
|
return mergeMap(
|
||||||
return source.pipe(mergeMap(
|
t => recognizeFn(
|
||||||
t => recognizeFn(
|
rootComponentType, config, t.urlAfterRedirects, serializer(t.urlAfterRedirects),
|
||||||
rootComponentType, config, t.urlAfterRedirects, serializer(t.urlAfterRedirects),
|
paramsInheritanceStrategy, relativeLinkResolution)
|
||||||
paramsInheritanceStrategy, relativeLinkResolution)
|
.pipe(map(targetSnapshot => ({...t, targetSnapshot}))));
|
||||||
.pipe(map(targetSnapshot => ({...t, targetSnapshot})))));
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,25 +19,23 @@ import {getToken} from '../utils/preactivation';
|
||||||
export function resolveData(
|
export function resolveData(
|
||||||
paramsInheritanceStrategy: 'emptyOnly'|'always',
|
paramsInheritanceStrategy: 'emptyOnly'|'always',
|
||||||
moduleInjector: Injector): MonoTypeOperatorFunction<NavigationTransition> {
|
moduleInjector: Injector): MonoTypeOperatorFunction<NavigationTransition> {
|
||||||
return function(source: Observable<NavigationTransition>) {
|
return mergeMap(t => {
|
||||||
return source.pipe(mergeMap(t => {
|
const {targetSnapshot, guards: {canActivateChecks}} = t;
|
||||||
const {targetSnapshot, guards: {canActivateChecks}} = t;
|
|
||||||
|
|
||||||
if (!canActivateChecks.length) {
|
if (!canActivateChecks.length) {
|
||||||
return of(t);
|
return of(t);
|
||||||
}
|
}
|
||||||
let canActivateChecksResolved = 0;
|
let canActivateChecksResolved = 0;
|
||||||
return from(canActivateChecks)
|
return from(canActivateChecks)
|
||||||
.pipe(
|
.pipe(
|
||||||
concatMap(
|
concatMap(
|
||||||
check => runResolve(
|
check => runResolve(
|
||||||
check.route, targetSnapshot!, paramsInheritanceStrategy, moduleInjector)),
|
check.route, targetSnapshot!, paramsInheritanceStrategy, moduleInjector)),
|
||||||
tap(() => canActivateChecksResolved++),
|
tap(() => canActivateChecksResolved++),
|
||||||
takeLast(1),
|
takeLast(1),
|
||||||
mergeMap(_ => canActivateChecksResolved === canActivateChecks.length ? of(t) : EMPTY),
|
mergeMap(_ => canActivateChecksResolved === canActivateChecks.length ? of(t) : EMPTY),
|
||||||
);
|
);
|
||||||
}));
|
});
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function runResolve(
|
function runResolve(
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {from, MonoTypeOperatorFunction, ObservableInput} from 'rxjs';
|
import {from, MonoTypeOperatorFunction, ObservableInput, of} from 'rxjs';
|
||||||
import {map, switchMap} from 'rxjs/operators';
|
import {map, switchMap} from 'rxjs/operators';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -17,13 +17,11 @@ import {map, switchMap} from 'rxjs/operators';
|
||||||
*/
|
*/
|
||||||
export function switchTap<T>(next: (x: T) => void|ObservableInput<any>):
|
export function switchTap<T>(next: (x: T) => void|ObservableInput<any>):
|
||||||
MonoTypeOperatorFunction<T> {
|
MonoTypeOperatorFunction<T> {
|
||||||
return function(source) {
|
return switchMap(v => {
|
||||||
return source.pipe(switchMap(v => {
|
const nextResult = next(v);
|
||||||
const nextResult = next(v);
|
if (nextResult) {
|
||||||
if (nextResult) {
|
return from(nextResult).pipe(map(() => v));
|
||||||
return from(nextResult).pipe(map(() => v));
|
}
|
||||||
}
|
return of(v);
|
||||||
return from([v]);
|
});
|
||||||
}));
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -573,12 +573,11 @@ export class Router {
|
||||||
if (transition !== this.transitions.getValue()) {
|
if (transition !== this.transitions.getValue()) {
|
||||||
return EMPTY;
|
return EMPTY;
|
||||||
}
|
}
|
||||||
return [t];
|
|
||||||
}),
|
|
||||||
|
|
||||||
// This delay is required to match old behavior that forced navigation
|
// This delay is required to match old behavior that forced
|
||||||
// to always be async
|
// navigation to always be async
|
||||||
switchMap(t => Promise.resolve(t)),
|
return Promise.resolve(t);
|
||||||
|
}),
|
||||||
|
|
||||||
// ApplyRedirects
|
// ApplyRedirects
|
||||||
applyRedirects(
|
applyRedirects(
|
||||||
|
@ -609,10 +608,8 @@ export class Router {
|
||||||
}
|
}
|
||||||
this.browserUrlTree = t.urlAfterRedirects;
|
this.browserUrlTree = t.urlAfterRedirects;
|
||||||
}
|
}
|
||||||
}),
|
|
||||||
|
|
||||||
// Fire RoutesRecognized
|
// Fire RoutesRecognized
|
||||||
tap(t => {
|
|
||||||
const routesRecognized = new RoutesRecognized(
|
const routesRecognized = new RoutesRecognized(
|
||||||
t.id, this.serializeUrl(t.extractedUrl),
|
t.id, this.serializeUrl(t.extractedUrl),
|
||||||
this.serializeUrl(t.urlAfterRedirects), t.targetSnapshot!);
|
this.serializeUrl(t.urlAfterRedirects), t.targetSnapshot!);
|
||||||
|
@ -692,9 +689,7 @@ export class Router {
|
||||||
error.url = t.guardsResult;
|
error.url = t.guardsResult;
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}),
|
|
||||||
|
|
||||||
tap(t => {
|
|
||||||
const guardsEnd = new GuardsCheckEnd(
|
const guardsEnd = new GuardsCheckEnd(
|
||||||
t.id, this.serializeUrl(t.extractedUrl),
|
t.id, this.serializeUrl(t.extractedUrl),
|
||||||
this.serializeUrl(t.urlAfterRedirects), t.targetSnapshot!,
|
this.serializeUrl(t.urlAfterRedirects), t.targetSnapshot!,
|
||||||
|
@ -875,7 +870,7 @@ export class Router {
|
||||||
replaceUrl: this.urlUpdateStrategy === 'eager'
|
replaceUrl: this.urlUpdateStrategy === 'eager'
|
||||||
};
|
};
|
||||||
|
|
||||||
return this.scheduleNavigation(
|
this.scheduleNavigation(
|
||||||
mergedTree, 'imperative', null, extras,
|
mergedTree, 'imperative', null, extras,
|
||||||
{resolve: t.resolve, reject: t.reject, promise: t.promise});
|
{resolve: t.resolve, reject: t.reject, promise: t.promise});
|
||||||
}, 0);
|
}, 0);
|
||||||
|
|
|
@ -8,9 +8,8 @@
|
||||||
|
|
||||||
import {ɵisObservable as isObservable, ɵisPromise as isPromise} from '@angular/core';
|
import {ɵisObservable as isObservable, ɵisPromise as isPromise} from '@angular/core';
|
||||||
import {from, Observable, of} from 'rxjs';
|
import {from, Observable, of} from 'rxjs';
|
||||||
import {concatAll, last as lastValue, map} from 'rxjs/operators';
|
|
||||||
|
|
||||||
import {Params, PRIMARY_OUTLET} from '../shared';
|
import {Params} from '../shared';
|
||||||
|
|
||||||
export function shallowEqualArrays(a: any[], b: any[]): boolean {
|
export function shallowEqualArrays(a: any[], b: any[]): boolean {
|
||||||
if (a.length !== b.length) return false;
|
if (a.length !== b.length) return false;
|
||||||
|
|
Loading…
Reference in New Issue