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
This commit is contained in:
parent
4c2fb1f6e8
commit
5e49d7e624
|
@ -249,9 +249,9 @@ export class RouteRegistry {
|
||||||
}
|
}
|
||||||
lastInstructionIsTerminal = lastInstruction.component.terminal;
|
lastInstructionIsTerminal = lastInstruction.component.terminal;
|
||||||
}
|
}
|
||||||
if (!lastInstructionIsTerminal) {
|
if (isPresent(componentCursor) && !lastInstructionIsTerminal) {
|
||||||
throw new BaseException(
|
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.`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -217,7 +217,8 @@ export class Router {
|
||||||
_settleInstruction(instruction: Instruction): Promise<any> {
|
_settleInstruction(instruction: Instruction): Promise<any> {
|
||||||
var unsettledInstructions: Array<Promise<any>> = [];
|
var unsettledInstructions: Array<Promise<any>> = [];
|
||||||
if (isBlank(instruction.component.componentType)) {
|
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)) {
|
if (isPresent(instruction.child)) {
|
||||||
unsettledInstructions.push(this._settleInstruction(instruction.child));
|
unsettledInstructions.push(this._settleInstruction(instruction.child));
|
||||||
|
|
|
@ -17,6 +17,7 @@ import {
|
||||||
} from 'angular2/test_lib';
|
} from 'angular2/test_lib';
|
||||||
|
|
||||||
import {NumberWrapper} from 'angular2/src/core/facade/lang';
|
import {NumberWrapper} from 'angular2/src/core/facade/lang';
|
||||||
|
import {PromiseWrapper} from 'angular2/src/core/facade/async';
|
||||||
|
|
||||||
import {bind, Component, DirectiveResolver, View} from 'angular2/core';
|
import {bind, Component, DirectiveResolver, View} from 'angular2/core';
|
||||||
|
|
||||||
|
@ -29,6 +30,7 @@ import {
|
||||||
Pipeline,
|
Pipeline,
|
||||||
RouterLink,
|
RouterLink,
|
||||||
RouterOutlet,
|
RouterOutlet,
|
||||||
|
AsyncRoute,
|
||||||
Route,
|
Route,
|
||||||
RouteParams,
|
RouteParams,
|
||||||
RouteConfig,
|
RouteConfig,
|
||||||
|
@ -96,7 +98,6 @@ export function main() {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
it('should generate link hrefs with params', inject([AsyncTestCompleter], (async) => {
|
it('should generate link hrefs with params', inject([AsyncTestCompleter], (async) => {
|
||||||
compile('<a href="hello" [router-link]="[\'./user\', {name: name}]">{{name}}</a>')
|
compile('<a href="hello" [router-link]="[\'./user\', {name: name}]">{{name}}</a>')
|
||||||
.then((_) => router.config(
|
.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',
|
it('should generate relative links preserving the existing parent route',
|
||||||
inject([AsyncTestCompleter], (async) => {
|
inject([AsyncTestCompleter], (async) => {
|
||||||
compile()
|
compile()
|
||||||
|
@ -319,6 +345,10 @@ class HelloCmp {
|
||||||
class Hello2Cmp {
|
class Hello2Cmp {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function parentCmpLoader() {
|
||||||
|
return PromiseWrapper.resolve(ParentCmp);
|
||||||
|
}
|
||||||
|
|
||||||
@Component({selector: 'parent-cmp'})
|
@Component({selector: 'parent-cmp'})
|
||||||
@View({
|
@View({
|
||||||
template: `{ <a [router-link]="['./grandchild']" class="grandchild-link">Grandchild</a>
|
template: `{ <a [router-link]="['./grandchild']" class="grandchild-link">Grandchild</a>
|
||||||
|
|
|
@ -235,7 +235,7 @@ export function main() {
|
||||||
registry.config(RootHostCmp,
|
registry.config(RootHostCmp,
|
||||||
new Route({path: '/first/...', component: DummyParentCmp, as: 'first'}));
|
new Route({path: '/first/...', component: DummyParentCmp, as: 'first'}));
|
||||||
expect(() => { registry.generate(['first'], RootHostCmp); })
|
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',
|
it('should match matrix params on child components and query params on the root component',
|
||||||
|
|
Loading…
Reference in New Issue