From 26d2ea8afc3190ee6e9269202d587fb5f3719295 Mon Sep 17 00:00:00 2001 From: Brian Ford Date: Fri, 14 Aug 2015 12:31:56 -0700 Subject: [PATCH] fix(router): fix regression with generating links to async routes Closes #3650 --- modules/angular2/src/router/router.ts | 24 ++++++++++++++++++-- modules/angular2/test/router/router_spec.ts | 25 ++++++++++++++++++--- 2 files changed, 44 insertions(+), 5 deletions(-) diff --git a/modules/angular2/src/router/router.ts b/modules/angular2/src/router/router.ts index 1d73035556..020c68bc7f 100644 --- a/modules/angular2/src/router/router.ts +++ b/modules/angular2/src/router/router.ts @@ -1,5 +1,5 @@ import {Promise, PromiseWrapper, EventEmitter, ObservableWrapper} from 'angular2/src/facade/async'; -import {Map, MapWrapper, List, ListWrapper} from 'angular2/src/facade/collection'; +import {Map, StringMapWrapper, MapWrapper, List, ListWrapper} from 'angular2/src/facade/collection'; import { isBlank, isString, @@ -131,7 +131,8 @@ export class Router { } _navigate(instruction: Instruction, _skipLocationChange: boolean): Promise { - return this._reuse(instruction) + return this._settleInstruction(instruction) + .then((_) => this._reuse(instruction)) .then((_) => this._canActivate(instruction)) .then((result) => { if (!result) { @@ -150,6 +151,25 @@ export class Router { }); } + // TODO(btford): it'd be nice to remove this method as part of cleaning up the traversal logic + // Since refactoring `Router.generate` to return an instruction rather than a string, it's not + // guaranteed that the `componentType`s for the terminal async routes have been loaded by the time + // we begin navigation. The method below simply traverses instructions and resolves any components + // for which `componentType` is not present + _settleInstruction(instruction: Instruction): Promise { + var unsettledInstructions: List> = []; + if (isBlank(instruction.component.componentType)) { + unsettledInstructions.push(instruction.component.resolveComponentType()); + } + if (isPresent(instruction.child)) { + unsettledInstructions.push(this._settleInstruction(instruction.child)); + } + StringMapWrapper.forEach(instruction.auxInstruction, (instruction, _) => { + unsettledInstructions.push(this._settleInstruction(instruction)); + }); + return PromiseWrapper.all(unsettledInstructions); + } + private _emitNavigationFinish(url): void { ObservableWrapper.callNext(this._subject, url); } private _afterPromiseFinishNavigating(promise: Promise): Promise { diff --git a/modules/angular2/test/router/router_spec.ts b/modules/angular2/test/router/router_spec.ts index 732b8c8abe..b800d6164d 100644 --- a/modules/angular2/test/router/router_spec.ts +++ b/modules/angular2/test/router/router_spec.ts @@ -11,10 +11,10 @@ import { beforeEachBindings, SpyObject } from 'angular2/test_lib'; -import {IMPLEMENTS} from 'angular2/src/facade/lang'; - +import {IMPLEMENTS, Type} from 'angular2/src/facade/lang'; import {Promise, PromiseWrapper} from 'angular2/src/facade/async'; import {ListWrapper} from 'angular2/src/facade/collection'; + import {Router, RootRouter} from 'angular2/src/router/router'; import {Pipeline} from 'angular2/src/router/pipeline'; import {RouterOutlet} from 'angular2/src/router/router_outlet'; @@ -23,7 +23,7 @@ import {Location} from 'angular2/src/router/location'; import {stringifyInstruction} from 'angular2/src/router/instruction'; import {RouteRegistry} from 'angular2/src/router/route_registry'; -import {RouteConfig, Route} from 'angular2/src/router/route_config_decorator'; +import {RouteConfig, AsyncRoute, Route} from 'angular2/src/router/route_config_decorator'; import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver'; import {bind} from 'angular2/di'; @@ -133,6 +133,21 @@ export function main() { expect(stringifyInstruction(instruction)).toEqual('first/second'); }); + it('should generate an instruction with terminal async routes', + inject([AsyncTestCompleter], (async) => { + var outlet = makeDummyOutlet(); + + router.registerOutlet(outlet); + router.config([new AsyncRoute({path: '/first', loader: loader, as: 'FirstCmp'})]); + + var instruction = router.generate(['/FirstCmp']); + router.navigateInstruction(instruction) + .then((_) => { + expect(outlet.spy('commit')).toHaveBeenCalled(); + async.done(); + }); + })); + describe('query string params', () => { it('should use query string params for the root route', () => { router.config( @@ -178,6 +193,10 @@ export function main() { }); } +function loader(): Promise { + return PromiseWrapper.resolve(DummyComponent); +} + @proxy @IMPLEMENTS(RouterOutlet) class DummyOutlet extends SpyObject {