refactor(router): misc

closes #14517
This commit is contained in:
Victor Berchet 2017-02-15 11:32:15 -08:00
parent 78e8814103
commit 5f3c8441e4
3 changed files with 118 additions and 118 deletions

View File

@ -79,11 +79,13 @@ class ApplyRedirects {
this.allowRedirects = false; this.allowRedirects = false;
// we need to run matching, so we can fetch all lazy-loaded modules // we need to run matching, so we can fetch all lazy-loaded modules
return this.match(e.urlTree); return this.match(e.urlTree);
} else if (e instanceof NoMatch) {
throw this.noMatchError(e);
} else {
throw e;
} }
if (e instanceof NoMatch) {
throw this.noMatchError(e);
}
throw e;
}); });
} }
@ -96,9 +98,9 @@ class ApplyRedirects {
return _catch.call(mapped$, (e: any): Observable<UrlTree> => { return _catch.call(mapped$, (e: any): Observable<UrlTree> => {
if (e instanceof NoMatch) { if (e instanceof NoMatch) {
throw this.noMatchError(e); throw this.noMatchError(e);
} else {
throw e;
} }
throw e;
}); });
} }
@ -121,10 +123,9 @@ class ApplyRedirects {
return map.call( return map.call(
this.expandChildren(injector, routes, segmentGroup), this.expandChildren(injector, routes, segmentGroup),
(children: any) => new UrlSegmentGroup([], children)); (children: any) => new UrlSegmentGroup([], children));
} else {
return this.expandSegment(
injector, segmentGroup, routes, segmentGroup.segments, outlet, true);
} }
return this.expandSegment(injector, segmentGroup, routes, segmentGroup.segments, outlet, true);
} }
private expandChildren(injector: Injector, routes: Route[], segmentGroup: UrlSegmentGroup): private expandChildren(injector: Injector, routes: Route[], segmentGroup: UrlSegmentGroup):
@ -142,10 +143,11 @@ class ApplyRedirects {
const expanded$ = this.expandSegmentAgainstRoute( const expanded$ = this.expandSegmentAgainstRoute(
injector, segmentGroup, routes, r, segments, outlet, allowRedirects); injector, segmentGroup, routes, r, segments, outlet, allowRedirects);
return _catch.call(expanded$, (e: any) => { return _catch.call(expanded$, (e: any) => {
if (e instanceof NoMatch) if (e instanceof NoMatch) {
return of (null); return of (null);
else }
throw e;
throw e;
}); });
}); });
const concattedProcessedRoutes$ = concatAll.call(processedRoutes$); const concattedProcessedRoutes$ = concatAll.call(processedRoutes$);
@ -154,12 +156,12 @@ class ApplyRedirects {
if (e instanceof EmptyError) { if (e instanceof EmptyError) {
if (this.noLeftoversInUrl(segmentGroup, segments, outlet)) { if (this.noLeftoversInUrl(segmentGroup, segments, outlet)) {
return of (new UrlSegmentGroup([], {})); return of (new UrlSegmentGroup([], {}));
} else {
throw new NoMatch(segmentGroup);
} }
} else {
throw e; throw new NoMatch(segmentGroup);
} }
throw e;
}); });
} }
@ -171,15 +173,20 @@ class ApplyRedirects {
private expandSegmentAgainstRoute( private expandSegmentAgainstRoute(
injector: Injector, segmentGroup: UrlSegmentGroup, routes: Route[], route: Route, injector: Injector, segmentGroup: UrlSegmentGroup, routes: Route[], route: Route,
paths: UrlSegment[], outlet: string, allowRedirects: boolean): Observable<UrlSegmentGroup> { paths: UrlSegment[], outlet: string, allowRedirects: boolean): Observable<UrlSegmentGroup> {
if (getOutlet(route) !== outlet) return noMatch(segmentGroup); if (getOutlet(route) !== outlet) {
if (route.redirectTo !== undefined && !(allowRedirects && this.allowRedirects))
return noMatch(segmentGroup); return noMatch(segmentGroup);
}
if (route.redirectTo !== undefined && !(allowRedirects && this.allowRedirects)) {
return noMatch(segmentGroup);
}
if (route.redirectTo === undefined) { if (route.redirectTo === undefined) {
return this.matchSegmentAgainstRoute(injector, segmentGroup, route, paths); return this.matchSegmentAgainstRoute(injector, segmentGroup, route, paths);
} else {
return this.expandSegmentAgainstRouteUsingRedirect(
injector, segmentGroup, routes, route, paths, outlet);
} }
return this.expandSegmentAgainstRouteUsingRedirect(
injector, segmentGroup, routes, route, paths, outlet);
} }
private expandSegmentAgainstRouteUsingRedirect( private expandSegmentAgainstRouteUsingRedirect(
@ -188,10 +195,10 @@ class ApplyRedirects {
if (route.path === '**') { if (route.path === '**') {
return this.expandWildCardWithParamsAgainstRouteUsingRedirect( return this.expandWildCardWithParamsAgainstRouteUsingRedirect(
injector, routes, route, outlet); injector, routes, route, outlet);
} else {
return this.expandRegularSegmentAgainstRouteUsingRedirect(
injector, segmentGroup, routes, route, segments, outlet);
} }
return this.expandRegularSegmentAgainstRouteUsingRedirect(
injector, segmentGroup, routes, route, segments, outlet);
} }
private expandWildCardWithParamsAgainstRouteUsingRedirect( private expandWildCardWithParamsAgainstRouteUsingRedirect(
@ -200,12 +207,12 @@ class ApplyRedirects {
const newTree = this.applyRedirectCommands([], route.redirectTo, {}); const newTree = this.applyRedirectCommands([], route.redirectTo, {});
if (route.redirectTo.startsWith('/')) { if (route.redirectTo.startsWith('/')) {
return absoluteRedirect(newTree); return absoluteRedirect(newTree);
} else {
return mergeMap.call(this.lineralizeSegments(route, newTree), (newSegments: UrlSegment[]) => {
const group = new UrlSegmentGroup(newSegments, {});
return this.expandSegment(injector, group, routes, newSegments, outlet, false);
});
} }
return mergeMap.call(this.lineralizeSegments(route, newTree), (newSegments: UrlSegment[]) => {
const group = new UrlSegmentGroup(newSegments, {});
return this.expandSegment(injector, group, routes, newSegments, outlet, false);
});
} }
private expandRegularSegmentAgainstRouteUsingRedirect( private expandRegularSegmentAgainstRouteUsingRedirect(
@ -219,13 +226,13 @@ class ApplyRedirects {
consumedSegments, route.redirectTo, <any>positionalParamSegments); consumedSegments, route.redirectTo, <any>positionalParamSegments);
if (route.redirectTo.startsWith('/')) { if (route.redirectTo.startsWith('/')) {
return absoluteRedirect(newTree); return absoluteRedirect(newTree);
} else {
return mergeMap.call(this.lineralizeSegments(route, newTree), (newSegments: UrlSegment[]) => {
return this.expandSegment(
injector, segmentGroup, routes, newSegments.concat(segments.slice(lastChild)), outlet,
false);
});
} }
return mergeMap.call(this.lineralizeSegments(route, newTree), (newSegments: UrlSegment[]) => {
return this.expandSegment(
injector, segmentGroup, routes, newSegments.concat(segments.slice(lastChild)), outlet,
false);
});
} }
private matchSegmentAgainstRoute( private matchSegmentAgainstRoute(
@ -233,66 +240,66 @@ class ApplyRedirects {
segments: UrlSegment[]): Observable<UrlSegmentGroup> { segments: UrlSegment[]): Observable<UrlSegmentGroup> {
if (route.path === '**') { if (route.path === '**') {
if (route.loadChildren) { if (route.loadChildren) {
return map.call(this.configLoader.load(injector, route), (r: any) => { return map.call(this.configLoader.load(injector, route), (cfg: LoadedRouterConfig) => {
(<any>route)._loadedConfig = r; (<any>route)._loadedConfig = cfg;
return new UrlSegmentGroup(segments, {}); return new UrlSegmentGroup(segments, {});
}); });
} else {
return of (new UrlSegmentGroup(segments, {}));
} }
} else { return of (new UrlSegmentGroup(segments, {}));
const {matched, consumedSegments, lastChild} = match(rawSegmentGroup, route, segments);
if (!matched) return noMatch(rawSegmentGroup);
const rawSlicedSegments = segments.slice(lastChild);
const childConfig$ = this.getChildConfig(injector, route);
return mergeMap.call(childConfig$, (routerConfig: any) => {
const childInjector = routerConfig.injector;
const childConfig = routerConfig.routes;
const {segmentGroup, slicedSegments} =
split(rawSegmentGroup, consumedSegments, rawSlicedSegments, childConfig);
if (slicedSegments.length === 0 && segmentGroup.hasChildren()) {
const expanded$ = this.expandChildren(childInjector, childConfig, segmentGroup);
return map.call(
expanded$, (children: any) => new UrlSegmentGroup(consumedSegments, children));
} else if (childConfig.length === 0 && slicedSegments.length === 0) {
return of (new UrlSegmentGroup(consumedSegments, {}));
} else {
const expanded$ = this.expandSegment(
childInjector, segmentGroup, childConfig, slicedSegments, PRIMARY_OUTLET, true);
return map.call(
expanded$,
(cs: any) => new UrlSegmentGroup(consumedSegments.concat(cs.segments), cs.children));
}
});
} }
const {matched, consumedSegments, lastChild} = match(rawSegmentGroup, route, segments);
if (!matched) return noMatch(rawSegmentGroup);
const rawSlicedSegments = segments.slice(lastChild);
const childConfig$ = this.getChildConfig(injector, route);
return mergeMap.call(childConfig$, (routerConfig: LoadedRouterConfig) => {
const childInjector = routerConfig.injector;
const childConfig = routerConfig.routes;
const {segmentGroup, slicedSegments} =
split(rawSegmentGroup, consumedSegments, rawSlicedSegments, childConfig);
if (slicedSegments.length === 0 && segmentGroup.hasChildren()) {
const expanded$ = this.expandChildren(childInjector, childConfig, segmentGroup);
return map.call(
expanded$, (children: any) => new UrlSegmentGroup(consumedSegments, children));
}
if (childConfig.length === 0 && slicedSegments.length === 0) {
return of (new UrlSegmentGroup(consumedSegments, {}));
}
const expanded$ = this.expandSegment(
childInjector, segmentGroup, childConfig, slicedSegments, PRIMARY_OUTLET, true);
return map.call(
expanded$, (cs: UrlSegmentGroup) =>
new UrlSegmentGroup(consumedSegments.concat(cs.segments), cs.children));
});
} }
private getChildConfig(injector: Injector, route: Route): Observable<LoadedRouterConfig> { private getChildConfig(injector: Injector, route: Route): Observable<LoadedRouterConfig> {
if (route.children) { if (route.children) {
return of (new LoadedRouterConfig(route.children, injector, null, null)); return of (new LoadedRouterConfig(route.children, injector, null, null));
} else if (route.loadChildren) {
return mergeMap.call(runGuards(injector, route), (shouldLoad: any) => {
if (shouldLoad) {
if ((<any>route)._loadedConfig) {
return of ((<any>route)._loadedConfig);
} else {
return map.call(this.configLoader.load(injector, route), (r: any) => {
(<any>route)._loadedConfig = r;
return r;
});
}
} else {
return canLoadFails(route);
}
});
} else {
return of (new LoadedRouterConfig([], injector, null, null));
} }
if (route.loadChildren) {
return mergeMap.call(runGuards(injector, route), (shouldLoad: any) => {
if (shouldLoad) {
return (<any>route)._loadedConfig ?
of ((<any>route)._loadedConfig) :
map.call(this.configLoader.load(injector, route), (cfg: LoadedRouterConfig) => {
(<any>route)._loadedConfig = cfg;
return cfg;
});
}
return canLoadFails(route);
});
}
return of (new LoadedRouterConfig([], injector, null, null));
} }
private lineralizeSegments(route: Route, urlTree: UrlTree): Observable<UrlSegment[]> { private lineralizeSegments(route: Route, urlTree: UrlTree): Observable<UrlSegment[]> {
@ -302,17 +309,18 @@ class ApplyRedirects {
res = res.concat(c.segments); res = res.concat(c.segments);
if (c.numberOfChildren === 0) { if (c.numberOfChildren === 0) {
return of (res); return of (res);
} else if (c.numberOfChildren > 1 || !c.children[PRIMARY_OUTLET]) {
return namedOutletsRedirect(route.redirectTo);
} else {
c = c.children[PRIMARY_OUTLET];
} }
if (c.numberOfChildren > 1 || !c.children[PRIMARY_OUTLET]) {
return namedOutletsRedirect(route.redirectTo);
}
c = c.children[PRIMARY_OUTLET];
} }
} }
private applyRedirectCommands( private applyRedirectCommands(
segments: UrlSegment[], redirectTo: string, posParams: {[k: string]: UrlSegment}): UrlTree { segments: UrlSegment[], redirectTo: string, posParams: {[k: string]: UrlSegment}): UrlTree {
const t = this.urlSerializer.parse(redirectTo);
return this.applyRedirectCreatreUrlTree( return this.applyRedirectCreatreUrlTree(
redirectTo, this.urlSerializer.parse(redirectTo), segments, posParams); redirectTo, this.urlSerializer.parse(redirectTo), segments, posParams);
} }
@ -329,11 +337,7 @@ class ApplyRedirects {
private createQueryParams(redirectToParams: Params, actualParams: Params): Params { private createQueryParams(redirectToParams: Params, actualParams: Params): Params {
const res: Params = {}; const res: Params = {};
forEach(redirectToParams, (v: any, k: string) => { forEach(redirectToParams, (v: any, k: string) => {
if (v.startsWith(':')) { res[k] = v.startsWith(':') ? actualParams[v.substring(1)] : v;
res[k] = actualParams[v.substring(1)];
} else {
res[k] = v;
}
}); });
return res; return res;
} }
@ -385,14 +389,12 @@ class ApplyRedirects {
function runGuards(injector: Injector, route: Route): Observable<boolean> { function runGuards(injector: Injector, route: Route): Observable<boolean> {
const canLoad = route.canLoad; const canLoad = route.canLoad;
if (!canLoad || canLoad.length === 0) return of (true); if (!canLoad || canLoad.length === 0) return of (true);
const obs = map.call(from(canLoad), (c: any) => { const obs = map.call(from(canLoad), (c: any) => {
const guard = injector.get(c); const guard = injector.get(c);
if (guard.canLoad) { return wrapIntoObservable(guard.canLoad ? guard.canLoad(route) : guard(route));
return wrapIntoObservable(guard.canLoad(route));
} else {
return wrapIntoObservable(guard(route));
}
}); });
return andObservables(obs); return andObservables(obs);
} }
@ -407,9 +409,9 @@ function match(segmentGroup: UrlSegmentGroup, route: Route, segments: UrlSegment
if (route.path === '') { if (route.path === '') {
if ((route.pathMatch === 'full') && (segmentGroup.hasChildren() || segments.length > 0)) { if ((route.pathMatch === 'full') && (segmentGroup.hasChildren() || segments.length > 0)) {
return {matched: false, consumedSegments: [], lastChild: 0, positionalParamSegments: {}}; return {matched: false, consumedSegments: [], lastChild: 0, positionalParamSegments: {}};
} else {
return {matched: true, consumedSegments: [], lastChild: 0, positionalParamSegments: {}};
} }
return {matched: true, consumedSegments: [], lastChild: 0, positionalParamSegments: {}};
} }
const matcher = route.matcher || defaultUrlMatcher; const matcher = route.matcher || defaultUrlMatcher;
@ -433,27 +435,26 @@ function split(
consumedSegments, createChildrenForEmptySegments( consumedSegments, createChildrenForEmptySegments(
config, new UrlSegmentGroup(slicedSegments, segmentGroup.children))); config, new UrlSegmentGroup(slicedSegments, segmentGroup.children)));
return {segmentGroup: mergeTrivialChildren(s), slicedSegments: []}; return {segmentGroup: mergeTrivialChildren(s), slicedSegments: []};
}
} else if ( if (slicedSegments.length === 0 &&
slicedSegments.length === 0 &&
containsEmptyPathRedirects(segmentGroup, slicedSegments, config)) { containsEmptyPathRedirects(segmentGroup, slicedSegments, config)) {
const s = new UrlSegmentGroup( const s = new UrlSegmentGroup(
segmentGroup.segments, addEmptySegmentsToChildrenIfNeeded( segmentGroup.segments, addEmptySegmentsToChildrenIfNeeded(
segmentGroup, slicedSegments, config, segmentGroup.children)); segmentGroup, slicedSegments, config, segmentGroup.children));
return {segmentGroup: mergeTrivialChildren(s), slicedSegments}; return {segmentGroup: mergeTrivialChildren(s), slicedSegments};
} else {
return {segmentGroup, slicedSegments};
} }
return {segmentGroup, slicedSegments};
} }
function mergeTrivialChildren(s: UrlSegmentGroup): UrlSegmentGroup { function mergeTrivialChildren(s: UrlSegmentGroup): UrlSegmentGroup {
if (s.numberOfChildren === 1 && s.children[PRIMARY_OUTLET]) { if (s.numberOfChildren === 1 && s.children[PRIMARY_OUTLET]) {
const c = s.children[PRIMARY_OUTLET]; const c = s.children[PRIMARY_OUTLET];
return new UrlSegmentGroup(s.segments.concat(c.segments), c.children); return new UrlSegmentGroup(s.segments.concat(c.segments), c.children);
} else {
return s;
} }
return s;
} }
function addEmptySegmentsToChildrenIfNeeded( function addEmptySegmentsToChildrenIfNeeded(
@ -496,8 +497,10 @@ function containsEmptyPathRedirects(
function emptyPathRedirect( function emptyPathRedirect(
segmentGroup: UrlSegmentGroup, slicedSegments: UrlSegment[], r: Route): boolean { 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 false;
}
return r.path === '' && r.redirectTo !== undefined; return r.path === '' && r.redirectTo !== undefined;
} }

View File

@ -14,6 +14,7 @@ export {RouterOutlet} from './directives/router_outlet';
export {Event, NavigationCancel, NavigationEnd, NavigationError, NavigationStart, RouteConfigLoadEnd, RouteConfigLoadStart, RoutesRecognized} from './events'; export {Event, NavigationCancel, NavigationEnd, NavigationError, NavigationStart, RouteConfigLoadEnd, RouteConfigLoadStart, RoutesRecognized} from './events';
export {CanActivate, CanActivateChild, CanDeactivate, CanLoad, Resolve} from './interfaces'; export {CanActivate, CanActivateChild, CanDeactivate, CanLoad, Resolve} from './interfaces';
export {DetachedRouteHandle, RouteReuseStrategy} from './route_reuse_strategy'; export {DetachedRouteHandle, RouteReuseStrategy} from './route_reuse_strategy';
export {NavigationExtras, Router} from './router';
export {ROUTES} from './router_config_loader'; export {ROUTES} from './router_config_loader';
export {ExtraOptions, ROUTER_CONFIGURATION, ROUTER_INITIALIZER, RouterModule, provideRoutes} from './router_module'; export {ExtraOptions, ROUTER_CONFIGURATION, ROUTER_INITIALIZER, RouterModule, provideRoutes} from './router_module';
export {RouterOutletMap} from './router_outlet_map'; export {RouterOutletMap} from './router_outlet_map';

View File

@ -421,14 +421,10 @@ export class Router {
*/ */
navigateByUrl(url: string|UrlTree, extras: NavigationExtras = {skipLocationChange: false}): navigateByUrl(url: string|UrlTree, extras: NavigationExtras = {skipLocationChange: false}):
Promise<boolean> { Promise<boolean> {
if (url instanceof UrlTree) { const urlTree = url instanceof UrlTree ? url : this.parseUrl(url);
return this.scheduleNavigation( const mergedTree = this.urlHandlingStrategy.merge(urlTree, this.rawUrlTree);
this.urlHandlingStrategy.merge(url, this.rawUrlTree), 'imperative', extras);
}
const urlTree = this.urlSerializer.parse(url); return this.scheduleNavigation(mergedTree, 'imperative', extras);
return this.scheduleNavigation(
this.urlHandlingStrategy.merge(urlTree, this.rawUrlTree), 'imperative', extras);
} }
/** /**
@ -517,8 +513,8 @@ export class Router {
} }
// Because of a bug in IE and Edge, the location class fires two events (popstate and // Because of a bug in IE and Edge, the location class fires two events (popstate and
// hashchange) // hashchange) every single time. The second one should be ignored. Otherwise, the URL will
// every single time. The second one should be ignored. Otherwise, the URL will flicker. // flicker.
if (lastNavigation && source == 'hashchange' && lastNavigation.source === 'popstate' && if (lastNavigation && source == 'hashchange' && lastNavigation.source === 'popstate' &&
lastNavigation.rawUrl.toString() === rawUrl.toString()) { lastNavigation.rawUrl.toString() === rawUrl.toString()) {
return null; // return value is not used return null; // return value is not used