diff --git a/packages/core/src/view/provider.ts b/packages/core/src/view/provider.ts index 866bedcc67..831d76a498 100644 --- a/packages/core/src/view/provider.ts +++ b/packages/core/src/view/provider.ts @@ -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); } diff --git a/packages/router/src/apply_redirects.ts b/packages/router/src/apply_redirects.ts index 5fcc94d901..f4bae709cc 100644 --- a/packages/router/src/apply_redirects.ts +++ b/packages/router/src/apply_redirects.ts @@ -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, rawSegmentGroup: UrlSegmentGroup, route: Route, + ngModule: NgModuleRef, rawSegmentGroup: UrlSegmentGroup, route: InternalRoute, segments: UrlSegment[]): Observable { if (route.path === '**') { if (route.loadChildren) { return map.call( this.configLoader.load(ngModule.injector, route), (cfg: LoadedRouterConfig) => { - (route)._loadedConfig = cfg; + route._loadedConfig = cfg; return new UrlSegmentGroup(segments, {}); }); } @@ -286,7 +286,8 @@ class ApplyRedirects { }); } - private getChildConfig(ngModule: NgModuleRef, route: Route): Observable { + private getChildConfig(ngModule: NgModuleRef, route: InternalRoute): + Observable { 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) => { - (route)._loadedConfig = cfg; + route._loadedConfig = cfg; return cfg; }); } diff --git a/packages/router/src/config.ts b/packages/router/src/config.ts index 9f41da34cf..6e0207d952 100644 --- a/packages/router/src/config.ts +++ b/packages/router/src/config.ts @@ -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++) { diff --git a/packages/router/src/private_export.ts b/packages/router/src/private_export.ts index 25c7d71a6f..1075f6e4ce 100644 --- a/packages/router/src/private_export.ts +++ b/packages/router/src/private_export.ts @@ -7,6 +7,5 @@ */ - export {ROUTER_PROVIDERS as ɵROUTER_PROVIDERS} from './router_module'; export {flatten as ɵflatten} from './utils/collection'; diff --git a/packages/router/src/recognize.ts b/packages/router/src/recognize.ts index d4c2c9d03c..169bd84484 100644 --- a/packages/router/src/recognize.ts +++ b/packages/router/src/recognize.ts @@ -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[] { 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[] { 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(snapshot, children)]; - - } else if (childConfig.length === 0 && slicedSegments.length === 0) { - return [new TreeNode(snapshot, [])]; - - } else { - const children = - this.processSegment(childConfig, segmentGroup, slicedSegments, PRIMARY_OUTLET); - return [new TreeNode(snapshot, children)]; } + + if (childConfig.length === 0 && slicedSegments.length === 0) { + return [new TreeNode(snapshot, [])]; + } + + const children = this.processSegment(childConfig, segmentGroup, slicedSegments, PRIMARY_OUTLET); + return [new TreeNode(snapshot, children)]; } } @@ -155,23 +154,25 @@ function sortActivatedRouteSnapshots(nodes: TreeNode[]): }); } -function getChildConfig(route: Route): Route[] { +function getChildConfig(route: InternalRoute): Route[] { if (route.children) { return route.children; - } else if (route.loadChildren) { - return (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 || {}; } diff --git a/packages/router/src/router.ts b/packages/router/src/router.ts index 59b548b099..d4ad6aa330 100644 --- a/packages/router/src/router.ts +++ b/packages/router/src/router.ts @@ -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, currNode: TreeNode, 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, k: string) => + this.deactiveRouteAndItsChildren(v, outletMap._outlets[k])); } private traverseRoutes( @@ -881,10 +881,10 @@ export class PreActivation { private deactiveRouteAndItsChildren( route: TreeNode, 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, k: string) => { if (!r.component) { this.deactiveRouteAndItsChildren(v, outlet); } else if (!!outlet) { @@ -1167,33 +1167,34 @@ function advanceActivatedRouteNodeAndItsChildren(node: TreeNode) } 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) { - return node ? node.children.reduce((m: any, c: TreeNode) => { - m[c.value.outlet] = c; - return m; - }, {}) : {}; +function nodeChildrenAsMap(node: TreeNode) { + const map: {[key: string]: TreeNode} = {}; + + if (node) { + node.children.forEach(child => map[child.value.outlet] = child); + } + + return map; } function getOutlet(outletMap: RouterOutletMap, route: ActivatedRoute): RouterOutlet { diff --git a/packages/router/src/router_preloader.ts b/packages/router/src/router_preloader.ts index ddc9b9dc29..45cf5a97d0 100644 --- a/packages/router/src/router_preloader.ts +++ b/packages/router/src/router_preloader.ts @@ -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, routes: Routes): Observable { const res: Observable[] = []; - 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 && (c)._loadedConfig) { - const childConfig = (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, route: Route): Observable { + private preloadConfig(ngModule: NgModuleRef, route: InternalRoute): Observable { 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); }); }); diff --git a/packages/router/src/url_tree.ts b/packages/router/src/url_tree.ts index ab99544b8c..d3cd499e85 100644 --- a/packages/router/src/url_tree.ts +++ b/packages/router/src/url_tree.ts @@ -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 { - constructor(public first: A, public second: B) {} -} - -function pairs(obj: {[key: string]: T}): Pair[] { - const res: Pair[] = []; - for (const prop in obj) { - if (obj.hasOwnProperty(prop)) { - res.push(new Pair(prop, obj[prop])); - } - } - return res; -} - const SEGMENT_RE = /^[^\/()?;=&#]+/; function matchSegments(str: string): string { SEGMENT_RE.lastIndex = 0;