From 5e49d7e624e010d89f64985f4b10bdfdb797fb91 Mon Sep 17 00:00:00 2001 From: Brian Ford Date: Fri, 11 Sep 2015 14:50:41 -0700 Subject: [PATCH] fix(router): load route config from async instructions Previously, async routes generated from links would not load the configs of their resolved components, which led to broken links in the children of the async instruction's component. This commit fixes the bookkeeping in the Router to correctly load the configs. Fixes internal b/23791558 Closes #4146 --- modules/angular2/src/router/route_registry.ts | 4 +-- modules/angular2/src/router/router.ts | 3 +- .../router/integration/router_link_spec.ts | 32 ++++++++++++++++++- .../test/router/route_registry_spec.ts | 2 +- 4 files changed, 36 insertions(+), 5 deletions(-) diff --git a/modules/angular2/src/router/route_registry.ts b/modules/angular2/src/router/route_registry.ts index ed9f639938..22eaeb5d21 100644 --- a/modules/angular2/src/router/route_registry.ts +++ b/modules/angular2/src/router/route_registry.ts @@ -249,9 +249,9 @@ export class RouteRegistry { } lastInstructionIsTerminal = lastInstruction.component.terminal; } - if (!lastInstructionIsTerminal) { + if (isPresent(componentCursor) && !lastInstructionIsTerminal) { throw new BaseException( - `Link "${ListWrapper.toJSON(linkParams)}" does not resolve to a terminal instruction.`); + `Link "${ListWrapper.toJSON(linkParams)}" does not resolve to a terminal or async instruction.`); } } diff --git a/modules/angular2/src/router/router.ts b/modules/angular2/src/router/router.ts index 68efc51a4c..4abba0c9f1 100644 --- a/modules/angular2/src/router/router.ts +++ b/modules/angular2/src/router/router.ts @@ -217,7 +217,8 @@ export class Router { _settleInstruction(instruction: Instruction): Promise { var unsettledInstructions: Array> = []; if (isBlank(instruction.component.componentType)) { - unsettledInstructions.push(instruction.component.resolveComponentType()); + unsettledInstructions.push(instruction.component.resolveComponentType().then( + (type: Type) => { this.registry.configFromComponent(type); })); } if (isPresent(instruction.child)) { unsettledInstructions.push(this._settleInstruction(instruction.child)); diff --git a/modules/angular2/test/router/integration/router_link_spec.ts b/modules/angular2/test/router/integration/router_link_spec.ts index f0353e14d2..e997065d95 100644 --- a/modules/angular2/test/router/integration/router_link_spec.ts +++ b/modules/angular2/test/router/integration/router_link_spec.ts @@ -17,6 +17,7 @@ import { } from 'angular2/test_lib'; import {NumberWrapper} from 'angular2/src/core/facade/lang'; +import {PromiseWrapper} from 'angular2/src/core/facade/async'; import {bind, Component, DirectiveResolver, View} from 'angular2/core'; @@ -29,6 +30,7 @@ import { Pipeline, RouterLink, RouterOutlet, + AsyncRoute, Route, RouteParams, RouteConfig, @@ -96,7 +98,6 @@ export function main() { })); - it('should generate link hrefs with params', inject([AsyncTestCompleter], (async) => { compile('{{name}}') .then((_) => router.config( @@ -128,6 +129,31 @@ export function main() { }); })); + it('should generate link hrefs when asynchronously loaded', + inject([AsyncTestCompleter], (async) => { + compile() + .then((_) => router.config([ + new AsyncRoute({ + path: '/child-with-grandchild/...', + loader: parentCmpLoader, + as: 'child-with-grandchild' + }) + ])) + .then((_) => { + // TODO: refactor when https://github.com/angular/angular/pull/4074 lands + var instruction = router.generate(['/child-with-grandchild']); + return router.navigateInstruction(instruction); + }) + .then((_) => { + rootTC.detectChanges(); + expect(DOM.getAttribute( + rootTC.componentViewChildren[1].componentViewChildren[0].nativeElement, + 'href')) + .toEqual('/child-with-grandchild/grandchild'); + async.done(); + }); + })); + it('should generate relative links preserving the existing parent route', inject([AsyncTestCompleter], (async) => { compile() @@ -319,6 +345,10 @@ class HelloCmp { class Hello2Cmp { } +function parentCmpLoader() { + return PromiseWrapper.resolve(ParentCmp); +} + @Component({selector: 'parent-cmp'}) @View({ template: `{ Grandchild diff --git a/modules/angular2/test/router/route_registry_spec.ts b/modules/angular2/test/router/route_registry_spec.ts index 4ee6271a60..0d20d75c59 100644 --- a/modules/angular2/test/router/route_registry_spec.ts +++ b/modules/angular2/test/router/route_registry_spec.ts @@ -235,7 +235,7 @@ export function main() { registry.config(RootHostCmp, new Route({path: '/first/...', component: DummyParentCmp, as: 'first'})); expect(() => { registry.generate(['first'], RootHostCmp); }) - .toThrowError('Link "["first"]" does not resolve to a terminal instruction.'); + .toThrowError('Link "["first"]" does not resolve to a terminal or async instruction.'); }); it('should match matrix params on child components and query params on the root component',