refactor(router): cleanup & simplification (#15436)

This commit is contained in:
Victor Berchet 2017-03-29 09:44:04 -07:00 committed by GitHub
parent 910c0d9ee7
commit d58a242fe7
8 changed files with 95 additions and 105 deletions

View File

@ -162,43 +162,43 @@ export function checkAndUpdateDirectiveInline(
if (bindLen > 0 && checkBinding(view, def, 0, v0)) {
changed = true;
changes = updateProp(view, providerData, def, 0, v0, changes);
};
}
if (bindLen > 1 && checkBinding(view, def, 1, v1)) {
changed = true;
changes = updateProp(view, providerData, def, 1, v1, changes);
};
}
if (bindLen > 2 && checkBinding(view, def, 2, v2)) {
changed = true;
changes = updateProp(view, providerData, def, 2, v2, changes);
};
}
if (bindLen > 3 && checkBinding(view, def, 3, v3)) {
changed = true;
changes = updateProp(view, providerData, def, 3, v3, changes);
};
}
if (bindLen > 4 && checkBinding(view, def, 4, v4)) {
changed = true;
changes = updateProp(view, providerData, def, 4, v4, changes);
};
}
if (bindLen > 5 && checkBinding(view, def, 5, v5)) {
changed = true;
changes = updateProp(view, providerData, def, 5, v5, changes);
};
}
if (bindLen > 6 && checkBinding(view, def, 6, v6)) {
changed = true;
changes = updateProp(view, providerData, def, 6, v6, changes);
};
}
if (bindLen > 7 && checkBinding(view, def, 7, v7)) {
changed = true;
changes = updateProp(view, providerData, def, 7, v7, changes);
};
}
if (bindLen > 8 && checkBinding(view, def, 8, v8)) {
changed = true;
changes = updateProp(view, providerData, def, 8, v8, changes);
};
}
if (bindLen > 9 && checkBinding(view, def, 9, v9)) {
changed = true;
changes = updateProp(view, providerData, def, 9, v9, changes);
};
}
if (changes) {
directive.ngOnChanges(changes);
}

View File

@ -18,7 +18,7 @@ import {map} from 'rxjs/operator/map';
import {mergeMap} from 'rxjs/operator/mergeMap';
import {EmptyError} from 'rxjs/util/EmptyError';
import {Route, Routes} from './config';
import {InternalRoute, Route, Routes} from './config';
import {LoadedRouterConfig, RouterConfigLoader} from './router_config_loader';
import {PRIMARY_OUTLET, Params, defaultUrlMatcher, navigationCancelingError} from './shared';
import {UrlSegment, UrlSegmentGroup, UrlSerializer, UrlTree} from './url_tree';
@ -241,13 +241,13 @@ class ApplyRedirects {
}
private matchSegmentAgainstRoute(
ngModule: NgModuleRef<any>, rawSegmentGroup: UrlSegmentGroup, route: Route,
ngModule: NgModuleRef<any>, rawSegmentGroup: UrlSegmentGroup, route: InternalRoute,
segments: UrlSegment[]): Observable<UrlSegmentGroup> {
if (route.path === '**') {
if (route.loadChildren) {
return map.call(
this.configLoader.load(ngModule.injector, route), (cfg: LoadedRouterConfig) => {
(<any>route)._loadedConfig = cfg;
route._loadedConfig = cfg;
return new UrlSegmentGroup(segments, {});
});
}
@ -286,7 +286,8 @@ class ApplyRedirects {
});
}
private getChildConfig(ngModule: NgModuleRef<any>, route: Route): Observable<LoadedRouterConfig> {
private getChildConfig(ngModule: NgModuleRef<any>, route: InternalRoute):
Observable<LoadedRouterConfig> {
if (route.children) {
// The children belong to the same module
return of (new LoadedRouterConfig(route.children, ngModule));
@ -302,7 +303,7 @@ class ApplyRedirects {
if (shouldLoad) {
return map.call(
this.configLoader.load(ngModule.injector, route), (cfg: LoadedRouterConfig) => {
(<any>route)._loadedConfig = cfg;
route._loadedConfig = cfg;
return cfg;
});
}

View File

@ -9,7 +9,6 @@
import {NgModuleFactory, Type} from '@angular/core';
import {Observable} from 'rxjs/Observable';
import {ActivatedRouteSnapshot, RouterStateSnapshot} from './router_state';
import {PRIMARY_OUTLET} from './shared';
import {UrlSegment, UrlSegmentGroup} from './url_tree';
@ -362,6 +361,11 @@ export interface Route {
runGuardsAndResolvers?: RunGuardsAndResolvers;
}
export interface InternalRoute extends Route {
// `LoadedRouterConfig` loaded via a Route `loadChildren`
_loadedConfig?: any;
}
export function validateConfig(config: Routes, parentPath: string = ''): void {
// forEach doesn't iterate undefined values
for (let i = 0; i < config.length; i++) {

View File

@ -7,6 +7,5 @@
*/
export {ROUTER_PROVIDERS as ɵROUTER_PROVIDERS} from './router_module';
export {flatten as ɵflatten} from './utils/collection';

View File

@ -11,7 +11,7 @@ import {Observable} from 'rxjs/Observable';
import {Observer} from 'rxjs/Observer';
import {of } from 'rxjs/observable/of';
import {Data, ResolveData, Route, Routes} from './config';
import {Data, InternalRoute, ResolveData, Route, Routes} from './config';
import {ActivatedRouteSnapshot, RouterStateSnapshot, inheritedParamsDataResolve} from './router_state';
import {PRIMARY_OUTLET, defaultUrlMatcher} from './shared';
import {UrlSegment, UrlSegmentGroup, UrlTree, mapChildrenIntoArray} from './url_tree';
@ -66,9 +66,9 @@ class Recognizer {
TreeNode<ActivatedRouteSnapshot>[] {
if (segmentGroup.segments.length === 0 && segmentGroup.hasChildren()) {
return this.processChildren(config, segmentGroup);
} else {
return this.processSegment(config, segmentGroup, segmentGroup.segments, outlet);
}
return this.processSegment(config, segmentGroup, segmentGroup.segments, outlet);
}
processChildren(config: Route[], segmentGroup: UrlSegmentGroup):
@ -92,9 +92,9 @@ class Recognizer {
}
if (this.noLeftoversInUrl(segmentGroup, segments, outlet)) {
return [];
} else {
throw new NoMatch();
}
throw new NoMatch();
}
private noLeftoversInUrl(segmentGroup: UrlSegmentGroup, segments: UrlSegment[], outlet: string):
@ -107,7 +107,7 @@ class Recognizer {
outlet: string): TreeNode<ActivatedRouteSnapshot>[] {
if (route.redirectTo) throw new NoMatch();
if ((route.outlet ? route.outlet : PRIMARY_OUTLET) !== outlet) throw new NoMatch();
if ((route.outlet || PRIMARY_OUTLET) !== outlet) throw new NoMatch();
if (route.path === '**') {
const params = segments.length > 0 ? last(segments).parameters : {};
@ -135,15 +135,14 @@ class Recognizer {
if (slicedSegments.length === 0 && segmentGroup.hasChildren()) {
const children = this.processChildren(childConfig, segmentGroup);
return [new TreeNode<ActivatedRouteSnapshot>(snapshot, children)];
} else if (childConfig.length === 0 && slicedSegments.length === 0) {
return [new TreeNode<ActivatedRouteSnapshot>(snapshot, [])];
} else {
const children =
this.processSegment(childConfig, segmentGroup, slicedSegments, PRIMARY_OUTLET);
return [new TreeNode<ActivatedRouteSnapshot>(snapshot, children)];
}
if (childConfig.length === 0 && slicedSegments.length === 0) {
return [new TreeNode<ActivatedRouteSnapshot>(snapshot, [])];
}
const children = this.processSegment(childConfig, segmentGroup, slicedSegments, PRIMARY_OUTLET);
return [new TreeNode<ActivatedRouteSnapshot>(snapshot, children)];
}
}
@ -155,23 +154,25 @@ function sortActivatedRouteSnapshots(nodes: TreeNode<ActivatedRouteSnapshot>[]):
});
}
function getChildConfig(route: Route): Route[] {
function getChildConfig(route: InternalRoute): Route[] {
if (route.children) {
return route.children;
} else if (route.loadChildren) {
return (<any>route)._loadedConfig.routes;
} else {
return [];
}
if (route.loadChildren) {
return route._loadedConfig.routes;
}
return [];
}
function match(segmentGroup: UrlSegmentGroup, route: Route, segments: UrlSegment[]) {
if (route.path === '') {
if (route.pathMatch === 'full' && (segmentGroup.hasChildren() || segments.length > 0)) {
throw new NoMatch();
} else {
return {consumedSegments: [], lastChild: 0, parameters: {}};
}
return {consumedSegments: [], lastChild: 0, parameters: {}};
}
const matcher = route.matcher || defaultUrlMatcher;
@ -228,9 +229,9 @@ function split(
s._sourceSegment = segmentGroup;
s._segmentIndexShift = consumedSegments.length;
return {segmentGroup: s, slicedSegments: []};
}
} else if (
slicedSegments.length === 0 &&
if (slicedSegments.length === 0 &&
containsEmptyPathMatches(segmentGroup, slicedSegments, config)) {
const s = new UrlSegmentGroup(
segmentGroup.segments, addEmptyPathsToChildrenIfNeeded(
@ -238,13 +239,12 @@ function split(
s._sourceSegment = segmentGroup;
s._segmentIndexShift = consumedSegments.length;
return {segmentGroup: s, slicedSegments};
} else {
const s = new UrlSegmentGroup(segmentGroup.segments, segmentGroup.children);
s._sourceSegment = segmentGroup;
s._segmentIndexShift = consumedSegments.length;
return {segmentGroup: s, slicedSegments};
}
const s = new UrlSegmentGroup(segmentGroup.segments, segmentGroup.children);
s._sourceSegment = segmentGroup;
s._segmentIndexShift = consumedSegments.length;
return {segmentGroup: s, slicedSegments};
}
function addEmptyPathsToChildrenIfNeeded(
@ -283,33 +283,32 @@ function createChildrenForEmptyPaths(
function containsEmptyPathMatchesWithNamedOutlets(
segmentGroup: UrlSegmentGroup, slicedSegments: UrlSegment[], routes: Route[]): boolean {
return routes
.filter(
r => emptyPathMatch(segmentGroup, slicedSegments, r) &&
getOutlet(r) !== PRIMARY_OUTLET)
.length > 0;
return routes.some(
r => emptyPathMatch(segmentGroup, slicedSegments, r) && getOutlet(r) !== PRIMARY_OUTLET);
}
function containsEmptyPathMatches(
segmentGroup: UrlSegmentGroup, slicedSegments: UrlSegment[], routes: Route[]): boolean {
return routes.filter(r => emptyPathMatch(segmentGroup, slicedSegments, r)).length > 0;
return routes.some(r => emptyPathMatch(segmentGroup, slicedSegments, r));
}
function emptyPathMatch(
segmentGroup: UrlSegmentGroup, slicedSegments: UrlSegment[], r: Route): boolean {
if ((segmentGroup.hasChildren() || slicedSegments.length > 0) && r.pathMatch === 'full')
if ((segmentGroup.hasChildren() || slicedSegments.length > 0) && r.pathMatch === 'full') {
return false;
}
return r.path === '' && r.redirectTo === undefined;
}
function getOutlet(route: Route): string {
return route.outlet ? route.outlet : PRIMARY_OUTLET;
return route.outlet || PRIMARY_OUTLET;
}
function getData(route: Route): Data {
return route.data ? route.data : {};
return route.data || {};
}
function getResolve(route: Route): ResolveData {
return route.resolve ? route.resolve : {};
return route.resolve || {};
}

View File

@ -22,7 +22,7 @@ import {mergeMap} from 'rxjs/operator/mergeMap';
import {reduce} from 'rxjs/operator/reduce';
import {applyRedirects} from './apply_redirects';
import {QueryParamsHandling, ResolveData, Route, Routes, RunGuardsAndResolvers, validateConfig} from './config';
import {InternalRoute, QueryParamsHandling, ResolveData, Route, Routes, RunGuardsAndResolvers, validateConfig} from './config';
import {createRouterState} from './create_router_state';
import {createUrlTree} from './create_url_tree';
import {RouterOutlet} from './directives/router_outlet';
@ -806,15 +806,15 @@ export class PreActivation {
private traverseChildRoutes(
futureNode: TreeNode<ActivatedRouteSnapshot>, currNode: TreeNode<ActivatedRouteSnapshot>,
outletMap: RouterOutletMap, futurePath: ActivatedRouteSnapshot[]): void {
const prevChildren: {[key: string]: any} = nodeChildrenAsMap(currNode);
const prevChildren = nodeChildrenAsMap(currNode);
futureNode.children.forEach(c => {
this.traverseRoutes(c, prevChildren[c.value.outlet], outletMap, futurePath.concat([c.value]));
delete prevChildren[c.value.outlet];
});
forEach(
prevChildren,
(v: any, k: string) => this.deactiveRouteAndItsChildren(v, outletMap._outlets[k]));
prevChildren, (v: TreeNode<ActivatedRouteSnapshot>, k: string) =>
this.deactiveRouteAndItsChildren(v, outletMap._outlets[k]));
}
private traverseRoutes(
@ -881,10 +881,10 @@ export class PreActivation {
private deactiveRouteAndItsChildren(
route: TreeNode<ActivatedRouteSnapshot>, outlet: RouterOutlet): void {
const prevChildren: {[key: string]: any} = nodeChildrenAsMap(route);
const prevChildren = nodeChildrenAsMap(route);
const r = route.value;
forEach(prevChildren, (v: any, k: string) => {
forEach(prevChildren, (v: TreeNode<ActivatedRouteSnapshot>, k: string) => {
if (!r.component) {
this.deactiveRouteAndItsChildren(v, outlet);
} else if (!!outlet) {
@ -1167,33 +1167,34 @@ function advanceActivatedRouteNodeAndItsChildren(node: TreeNode<ActivatedRoute>)
}
function parentLoadedConfig(snapshot: ActivatedRouteSnapshot): LoadedRouterConfig {
let s = snapshot.parent;
while (s) {
const c: any = s._routeConfig;
if (c && c._loadedConfig) return c._loadedConfig;
if (c && c.component) return null;
s = s.parent;
for (let s = snapshot.parent; s; s = s.parent) {
const route: InternalRoute = s._routeConfig;
if (route && route._loadedConfig) return route._loadedConfig;
if (route && route.component) return null;
}
return null;
}
function closestLoadedConfig(snapshot: ActivatedRouteSnapshot): LoadedRouterConfig {
if (!snapshot) return null;
let s = snapshot.parent;
while (s) {
const c: any = s._routeConfig;
if (c && c._loadedConfig) return c._loadedConfig;
s = s.parent;
for (let s = snapshot.parent; s; s = s.parent) {
const route: InternalRoute = s._routeConfig;
if (route && route._loadedConfig) return route._loadedConfig;
}
return null;
}
function nodeChildrenAsMap(node: TreeNode<any>) {
return node ? node.children.reduce((m: any, c: TreeNode<any>) => {
m[c.value.outlet] = c;
return m;
}, {}) : {};
function nodeChildrenAsMap<T extends{outlet: string}>(node: TreeNode<T>) {
const map: {[key: string]: TreeNode<T>} = {};
if (node) {
node.children.forEach(child => map[child.value.outlet] = child);
}
return map;
}
function getOutlet(outletMap: RouterOutletMap, route: ActivatedRoute): RouterOutlet {

View File

@ -16,7 +16,7 @@ import {concatMap} from 'rxjs/operator/concatMap';
import {filter} from 'rxjs/operator/filter';
import {mergeAll} from 'rxjs/operator/mergeAll';
import {mergeMap} from 'rxjs/operator/mergeMap';
import {Route, Routes} from './config';
import {InternalRoute, Route, Routes} from './config';
import {NavigationEnd, RouteConfigLoadEnd, RouteConfigLoadStart} from './events';
import {Router} from './router';
import {RouterConfigLoader} from './router_config_loader';
@ -100,30 +100,30 @@ export class RouterPreloader {
private processRoutes(ngModule: NgModuleRef<any>, routes: Routes): Observable<void> {
const res: Observable<any>[] = [];
for (const c of routes) {
for (const r of routes) {
const route: InternalRoute = r;
// we already have the config loaded, just recurse
if (c.loadChildren && !c.canLoad && (<any>c)._loadedConfig) {
const childConfig = (<any>c)._loadedConfig;
res.push(this.processRoutes(childConfig.module, childConfig.routes));
if (route.loadChildren && !route.canLoad && route._loadedConfig) {
const childConfig = route._loadedConfig;
res.push(this.processRoutes(ngModule, childConfig.routes));
// no config loaded, fetch the config
} else if (c.loadChildren && !c.canLoad) {
res.push(this.preloadConfig(ngModule, c));
} else if (route.loadChildren && !route.canLoad) {
res.push(this.preloadConfig(ngModule, route));
// recurse into children
} else if (c.children) {
res.push(this.processRoutes(ngModule, c.children));
} else if (route.children) {
res.push(this.processRoutes(ngModule, route.children));
}
}
return mergeAll.call(from(res));
}
private preloadConfig(ngModule: NgModuleRef<any>, route: Route): Observable<void> {
private preloadConfig(ngModule: NgModuleRef<any>, route: InternalRoute): Observable<void> {
return this.preloadingStrategy.preload(route, () => {
const loaded = this.loader.load(ngModule.injector, route);
return mergeMap.call(loaded, (config: any): any => {
const c: any = route;
c._loadedConfig = config;
route._loadedConfig = config;
return this.processRoutes(config.module, config.routes);
});
});

View File

@ -345,7 +345,7 @@ export function serializePath(path: UrlSegment): string {
}
function serializeParams(params: {[key: string]: string}): string {
return pairs(params).map(p => `;${encode(p.first)}=${encode(p.second)}`).join('');
return Object.keys(params).map(key => `;${encode(key)}=${encode(params[key])}`).join('');
}
function serializeQueryParams(params: {[key: string]: any}): string {
@ -358,20 +358,6 @@ function serializeQueryParams(params: {[key: string]: any}): string {
return strParams.length ? `?${strParams.join("&")}` : '';
}
class Pair<A, B> {
constructor(public first: A, public second: B) {}
}
function pairs<T>(obj: {[key: string]: T}): Pair<string, T>[] {
const res: Pair<string, T>[] = [];
for (const prop in obj) {
if (obj.hasOwnProperty(prop)) {
res.push(new Pair<string, T>(prop, obj[prop]));
}
}
return res;
}
const SEGMENT_RE = /^[^\/()?;=&#]+/;
function matchSegments(str: string): string {
SEGMENT_RE.lastIndex = 0;