From d930ad1816ca0b0ee8b3068a58c7558d21c8c00a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=C5=A1ko=20Hevery?= Date: Mon, 2 May 2016 17:11:21 +0000 Subject: [PATCH] chore: router move-only --- .../{router => router_deprecated}/ts/.gitkeep | 0 .../ts/can_activate/can_activate_example.ts | 0 .../ts/can_activate/can_activate_spec.ts | 0 .../ts/can_activate/index.html | 0 .../can_deactivate/can_deactivate_example.ts | 0 .../ts/can_deactivate/can_deactivate_spec.ts | 0 .../ts/can_deactivate/index.html | 0 .../ts/on_activate/on_activate_example.ts | 0 .../ts/on_deactivate/index.html | 0 .../ts/on_deactivate/on_deactivate_example.ts | 0 .../ts/on_deactivate/on_deactivate_spec.ts | 0 .../ts/reuse/index.html | 0 .../ts/reuse/reuse_example.ts | 0 .../ts/reuse/reuse_spec.ts | 0 .../core_private.dart | 0 .../core_private.ts | 0 modules/@angular/router-deprecated/index.ts | 1 + .../package.json | 0 .../rollup.config.js | 0 .../{router => router-deprecated}/router.ts | 0 .../src/directives/router_link.ts | 83 ++ .../src/directives/router_outlet.ts | 176 +++++ .../{router => router-deprecated}/src/facade | 0 .../src/instruction.ts | 0 .../router-deprecated/src/interfaces.ts | 124 +++ .../src/lifecycle/lifecycle_annotations.dart | 0 .../src/lifecycle/lifecycle_annotations.ts | 0 .../lifecycle/lifecycle_annotations_impl.ts | 0 .../lifecycle/route_lifecycle_reflector.dart | 0 .../lifecycle/route_lifecycle_reflector.ts | 0 .../src/package.json | 0 .../route_config/route_config_decorator.dart | 0 .../route_config/route_config_decorator.ts | 0 .../src/route_config/route_config_impl.ts | 0 .../route_config/route_config_normalizer.dart | 0 .../route_config/route_config_normalizer.ts | 0 .../src/route_definition.dart | 0 .../src/route_definition.ts | 0 .../src/route_registry.ts | 0 .../@angular/router-deprecated/src/router.ts | 578 ++++++++++++++ .../router-deprecated/src/router_providers.ts | 41 + .../src/router_providers_common.ts | 39 + .../route_handlers/async_route_handler.ts | 0 .../src/rules/route_handlers/route_handler.ts | 0 .../route_handlers/sync_route_handler.ts | 0 .../src/rules/route_paths/param_route_path.ts | 0 .../src/rules/route_paths/regex_route_path.ts | 0 .../src/rules/route_paths/route_path.ts | 0 .../src/rules/rule_set.ts | 0 .../src/rules/rules.ts | 0 .../src/url_parser.ts | 0 .../src/utils.ts | 0 .../test/directives/router_link_spec.ts | 0 .../test/integration/README.md | 0 .../test/integration/async_route_spec.ts | 0 .../test/integration/auxiliary_route_spec.ts | 0 .../test/integration/bootstrap_spec.ts | 0 .../integration/impl/async_route_spec_impl.ts | 0 .../integration/impl/aux_route_spec_impl.ts | 0 .../integration/impl/fixture_components.ts | 0 .../integration/impl/sync_route_spec_impl.ts | 0 .../test/integration/lifecycle_hook_spec.ts | 0 .../test/integration/navigation_spec.ts | 0 .../test/integration/redirect_route_spec.ts | 0 .../test/integration/router_link_spec.ts | 0 .../test/integration/sync_route_spec.ts | 0 .../test/integration/util.ts | 0 .../location/hash_location_strategy_spec.ts | 0 .../test/location/location_spec.ts | 0 .../location/path_location_strategy_spec.ts | 0 .../test/route_config/route_config_spec.dart | 0 .../test/route_config/route_config_spec.ts | 0 .../test/route_registry_spec.ts | 0 .../test/router_spec.ts | 0 .../route_paths/param_route_path_spec.ts | 0 .../route_paths/regex_route_param_spec.ts | 0 .../test/rules/rule_set_spec.ts | 0 .../test/spies.dart | 0 .../test/spies.ts | 0 .../test/url_parser_spec.ts | 0 .../{router => router-deprecated}/testing.ts | 0 .../tsconfig-es2015.json | 0 .../tsconfig.json | 0 modules/@angular/router/index.ts | 22 +- .../router/src}/constants.ts | 0 .../router/src}/core_private.ts | 0 .../router/src/directives/router_link.ts | 117 ++- .../router/src/directives/router_outlet.ts | 186 +---- modules/@angular/router/src/interfaces.ts | 124 +-- .../router/src}/lifecycle_reflector.dart | 0 .../router/src}/lifecycle_reflector.ts | 0 .../router/src}/link.ts | 0 .../router/src}/metadata/decorators.dart | 0 .../router/src}/metadata/decorators.ts | 0 .../router/src}/metadata/metadata.ts | 0 .../router/src}/recognize.ts | 0 modules/@angular/router/src/router.ts | 718 +++++------------- .../@angular/router/src/router_providers.ts | 35 +- .../router/src/router_providers_common.ts | 46 +- .../router/src}/router_url_serializer.ts | 0 .../router/src}/segments.ts | 0 .../router/test}/integration_spec.ts | 0 .../router/test}/link_spec.ts | 0 .../router/test}/recognize_spec.ts | 0 .../test}/router_url_serializer_spec.ts | 0 .../router/test}/tree_spec.ts | 0 .../testing}/router_testing_providers.ts | 0 modules/angular2/alt_router.ts | 21 - .../src/alt_router/directives/router_link.ts | 62 -- .../alt_router/directives/router_outlet.ts | 42 - modules/angular2/src/alt_router/interfaces.ts | 10 - modules/angular2/src/alt_router/router.ts | 226 ------ .../src/alt_router/router_providers.ts | 8 - .../src/alt_router/router_providers_common.ts | 31 - .../angular2/src/core/di/provider_util.dart | 20 - modules/angular2/src/core/di/provider_util.ts | 9 - 116 files changed, 1345 insertions(+), 1374 deletions(-) rename modules/@angular/examples/{router => router_deprecated}/ts/.gitkeep (100%) rename modules/@angular/examples/{router => router_deprecated}/ts/can_activate/can_activate_example.ts (100%) rename modules/@angular/examples/{router => router_deprecated}/ts/can_activate/can_activate_spec.ts (100%) rename modules/@angular/examples/{router => router_deprecated}/ts/can_activate/index.html (100%) rename modules/@angular/examples/{router => router_deprecated}/ts/can_deactivate/can_deactivate_example.ts (100%) rename modules/@angular/examples/{router => router_deprecated}/ts/can_deactivate/can_deactivate_spec.ts (100%) rename modules/@angular/examples/{router => router_deprecated}/ts/can_deactivate/index.html (100%) rename modules/@angular/examples/{router => router_deprecated}/ts/on_activate/on_activate_example.ts (100%) rename modules/@angular/examples/{router => router_deprecated}/ts/on_deactivate/index.html (100%) rename modules/@angular/examples/{router => router_deprecated}/ts/on_deactivate/on_deactivate_example.ts (100%) rename modules/@angular/examples/{router => router_deprecated}/ts/on_deactivate/on_deactivate_spec.ts (100%) rename modules/@angular/examples/{router => router_deprecated}/ts/reuse/index.html (100%) rename modules/@angular/examples/{router => router_deprecated}/ts/reuse/reuse_example.ts (100%) rename modules/@angular/examples/{router => router_deprecated}/ts/reuse/reuse_spec.ts (100%) rename modules/@angular/{router => router-deprecated}/core_private.dart (100%) rename modules/@angular/{router => router-deprecated}/core_private.ts (100%) create mode 100644 modules/@angular/router-deprecated/index.ts rename modules/@angular/{router => router-deprecated}/package.json (100%) rename modules/@angular/{router => router-deprecated}/rollup.config.js (100%) rename modules/@angular/{router => router-deprecated}/router.ts (100%) create mode 100644 modules/@angular/router-deprecated/src/directives/router_link.ts create mode 100644 modules/@angular/router-deprecated/src/directives/router_outlet.ts rename modules/@angular/{router => router-deprecated}/src/facade (100%) rename modules/@angular/{router => router-deprecated}/src/instruction.ts (100%) create mode 100644 modules/@angular/router-deprecated/src/interfaces.ts rename modules/@angular/{router => router-deprecated}/src/lifecycle/lifecycle_annotations.dart (100%) rename modules/@angular/{router => router-deprecated}/src/lifecycle/lifecycle_annotations.ts (100%) rename modules/@angular/{router => router-deprecated}/src/lifecycle/lifecycle_annotations_impl.ts (100%) rename modules/@angular/{router => router-deprecated}/src/lifecycle/route_lifecycle_reflector.dart (100%) rename modules/@angular/{router => router-deprecated}/src/lifecycle/route_lifecycle_reflector.ts (100%) rename modules/@angular/{router => router-deprecated}/src/package.json (100%) rename modules/@angular/{router => router-deprecated}/src/route_config/route_config_decorator.dart (100%) rename modules/@angular/{router => router-deprecated}/src/route_config/route_config_decorator.ts (100%) rename modules/@angular/{router => router-deprecated}/src/route_config/route_config_impl.ts (100%) rename modules/@angular/{router => router-deprecated}/src/route_config/route_config_normalizer.dart (100%) rename modules/@angular/{router => router-deprecated}/src/route_config/route_config_normalizer.ts (100%) rename modules/@angular/{router => router-deprecated}/src/route_definition.dart (100%) rename modules/@angular/{router => router-deprecated}/src/route_definition.ts (100%) rename modules/@angular/{router => router-deprecated}/src/route_registry.ts (100%) create mode 100644 modules/@angular/router-deprecated/src/router.ts create mode 100644 modules/@angular/router-deprecated/src/router_providers.ts create mode 100644 modules/@angular/router-deprecated/src/router_providers_common.ts rename modules/@angular/{router => router-deprecated}/src/rules/route_handlers/async_route_handler.ts (100%) rename modules/@angular/{router => router-deprecated}/src/rules/route_handlers/route_handler.ts (100%) rename modules/@angular/{router => router-deprecated}/src/rules/route_handlers/sync_route_handler.ts (100%) rename modules/@angular/{router => router-deprecated}/src/rules/route_paths/param_route_path.ts (100%) rename modules/@angular/{router => router-deprecated}/src/rules/route_paths/regex_route_path.ts (100%) rename modules/@angular/{router => router-deprecated}/src/rules/route_paths/route_path.ts (100%) rename modules/@angular/{router => router-deprecated}/src/rules/rule_set.ts (100%) rename modules/@angular/{router => router-deprecated}/src/rules/rules.ts (100%) rename modules/@angular/{router => router-deprecated}/src/url_parser.ts (100%) rename modules/@angular/{router => router-deprecated}/src/utils.ts (100%) rename modules/@angular/{router => router-deprecated}/test/directives/router_link_spec.ts (100%) rename modules/@angular/{router => router-deprecated}/test/integration/README.md (100%) rename modules/@angular/{router => router-deprecated}/test/integration/async_route_spec.ts (100%) rename modules/@angular/{router => router-deprecated}/test/integration/auxiliary_route_spec.ts (100%) rename modules/@angular/{router => router-deprecated}/test/integration/bootstrap_spec.ts (100%) rename modules/@angular/{router => router-deprecated}/test/integration/impl/async_route_spec_impl.ts (100%) rename modules/@angular/{router => router-deprecated}/test/integration/impl/aux_route_spec_impl.ts (100%) rename modules/@angular/{router => router-deprecated}/test/integration/impl/fixture_components.ts (100%) rename modules/@angular/{router => router-deprecated}/test/integration/impl/sync_route_spec_impl.ts (100%) rename modules/@angular/{router => router-deprecated}/test/integration/lifecycle_hook_spec.ts (100%) rename modules/@angular/{router => router-deprecated}/test/integration/navigation_spec.ts (100%) rename modules/@angular/{router => router-deprecated}/test/integration/redirect_route_spec.ts (100%) rename modules/@angular/{router => router-deprecated}/test/integration/router_link_spec.ts (100%) rename modules/@angular/{router => router-deprecated}/test/integration/sync_route_spec.ts (100%) rename modules/@angular/{router => router-deprecated}/test/integration/util.ts (100%) rename modules/@angular/{router => router-deprecated}/test/location/hash_location_strategy_spec.ts (100%) rename modules/@angular/{router => router-deprecated}/test/location/location_spec.ts (100%) rename modules/@angular/{router => router-deprecated}/test/location/path_location_strategy_spec.ts (100%) rename modules/@angular/{router => router-deprecated}/test/route_config/route_config_spec.dart (100%) rename modules/@angular/{router => router-deprecated}/test/route_config/route_config_spec.ts (100%) rename modules/@angular/{router => router-deprecated}/test/route_registry_spec.ts (100%) rename modules/@angular/{router => router-deprecated}/test/router_spec.ts (100%) rename modules/@angular/{router => router-deprecated}/test/rules/route_paths/param_route_path_spec.ts (100%) rename modules/@angular/{router => router-deprecated}/test/rules/route_paths/regex_route_param_spec.ts (100%) rename modules/@angular/{router => router-deprecated}/test/rules/rule_set_spec.ts (100%) rename modules/@angular/{router => router-deprecated}/test/spies.dart (100%) rename modules/@angular/{router => router-deprecated}/test/spies.ts (100%) rename modules/@angular/{router => router-deprecated}/test/url_parser_spec.ts (100%) rename modules/@angular/{router => router-deprecated}/testing.ts (100%) rename modules/@angular/{router => router-deprecated}/tsconfig-es2015.json (100%) rename modules/@angular/{router => router-deprecated}/tsconfig.json (100%) rename modules/{angular2/src/alt_router => @angular/router/src}/constants.ts (100%) rename modules/{angular2/src/alt_router => @angular/router/src}/core_private.ts (100%) rename modules/{angular2/src/alt_router => @angular/router/src}/lifecycle_reflector.dart (100%) rename modules/{angular2/src/alt_router => @angular/router/src}/lifecycle_reflector.ts (100%) rename modules/{angular2/src/alt_router => @angular/router/src}/link.ts (100%) rename modules/{angular2/src/alt_router => @angular/router/src}/metadata/decorators.dart (100%) rename modules/{angular2/src/alt_router => @angular/router/src}/metadata/decorators.ts (100%) rename modules/{angular2/src/alt_router => @angular/router/src}/metadata/metadata.ts (100%) rename modules/{angular2/src/alt_router => @angular/router/src}/recognize.ts (100%) rename modules/{angular2/src/alt_router => @angular/router/src}/router_url_serializer.ts (100%) rename modules/{angular2/src/alt_router => @angular/router/src}/segments.ts (100%) rename modules/{angular2/test/alt_router => @angular/router/test}/integration_spec.ts (100%) rename modules/{angular2/test/alt_router => @angular/router/test}/link_spec.ts (100%) rename modules/{angular2/test/alt_router => @angular/router/test}/recognize_spec.ts (100%) rename modules/{angular2/test/alt_router => @angular/router/test}/router_url_serializer_spec.ts (100%) rename modules/{angular2/test/alt_router => @angular/router/test}/tree_spec.ts (100%) rename modules/{angular2/src/alt_router => @angular/router/testing}/router_testing_providers.ts (100%) delete mode 100644 modules/angular2/alt_router.ts delete mode 100644 modules/angular2/src/alt_router/directives/router_link.ts delete mode 100644 modules/angular2/src/alt_router/directives/router_outlet.ts delete mode 100644 modules/angular2/src/alt_router/interfaces.ts delete mode 100644 modules/angular2/src/alt_router/router.ts delete mode 100644 modules/angular2/src/alt_router/router_providers.ts delete mode 100644 modules/angular2/src/alt_router/router_providers_common.ts delete mode 100644 modules/angular2/src/core/di/provider_util.dart delete mode 100644 modules/angular2/src/core/di/provider_util.ts diff --git a/modules/@angular/examples/router/ts/.gitkeep b/modules/@angular/examples/router_deprecated/ts/.gitkeep similarity index 100% rename from modules/@angular/examples/router/ts/.gitkeep rename to modules/@angular/examples/router_deprecated/ts/.gitkeep diff --git a/modules/@angular/examples/router/ts/can_activate/can_activate_example.ts b/modules/@angular/examples/router_deprecated/ts/can_activate/can_activate_example.ts similarity index 100% rename from modules/@angular/examples/router/ts/can_activate/can_activate_example.ts rename to modules/@angular/examples/router_deprecated/ts/can_activate/can_activate_example.ts diff --git a/modules/@angular/examples/router/ts/can_activate/can_activate_spec.ts b/modules/@angular/examples/router_deprecated/ts/can_activate/can_activate_spec.ts similarity index 100% rename from modules/@angular/examples/router/ts/can_activate/can_activate_spec.ts rename to modules/@angular/examples/router_deprecated/ts/can_activate/can_activate_spec.ts diff --git a/modules/@angular/examples/router/ts/can_activate/index.html b/modules/@angular/examples/router_deprecated/ts/can_activate/index.html similarity index 100% rename from modules/@angular/examples/router/ts/can_activate/index.html rename to modules/@angular/examples/router_deprecated/ts/can_activate/index.html diff --git a/modules/@angular/examples/router/ts/can_deactivate/can_deactivate_example.ts b/modules/@angular/examples/router_deprecated/ts/can_deactivate/can_deactivate_example.ts similarity index 100% rename from modules/@angular/examples/router/ts/can_deactivate/can_deactivate_example.ts rename to modules/@angular/examples/router_deprecated/ts/can_deactivate/can_deactivate_example.ts diff --git a/modules/@angular/examples/router/ts/can_deactivate/can_deactivate_spec.ts b/modules/@angular/examples/router_deprecated/ts/can_deactivate/can_deactivate_spec.ts similarity index 100% rename from modules/@angular/examples/router/ts/can_deactivate/can_deactivate_spec.ts rename to modules/@angular/examples/router_deprecated/ts/can_deactivate/can_deactivate_spec.ts diff --git a/modules/@angular/examples/router/ts/can_deactivate/index.html b/modules/@angular/examples/router_deprecated/ts/can_deactivate/index.html similarity index 100% rename from modules/@angular/examples/router/ts/can_deactivate/index.html rename to modules/@angular/examples/router_deprecated/ts/can_deactivate/index.html diff --git a/modules/@angular/examples/router/ts/on_activate/on_activate_example.ts b/modules/@angular/examples/router_deprecated/ts/on_activate/on_activate_example.ts similarity index 100% rename from modules/@angular/examples/router/ts/on_activate/on_activate_example.ts rename to modules/@angular/examples/router_deprecated/ts/on_activate/on_activate_example.ts diff --git a/modules/@angular/examples/router/ts/on_deactivate/index.html b/modules/@angular/examples/router_deprecated/ts/on_deactivate/index.html similarity index 100% rename from modules/@angular/examples/router/ts/on_deactivate/index.html rename to modules/@angular/examples/router_deprecated/ts/on_deactivate/index.html diff --git a/modules/@angular/examples/router/ts/on_deactivate/on_deactivate_example.ts b/modules/@angular/examples/router_deprecated/ts/on_deactivate/on_deactivate_example.ts similarity index 100% rename from modules/@angular/examples/router/ts/on_deactivate/on_deactivate_example.ts rename to modules/@angular/examples/router_deprecated/ts/on_deactivate/on_deactivate_example.ts diff --git a/modules/@angular/examples/router/ts/on_deactivate/on_deactivate_spec.ts b/modules/@angular/examples/router_deprecated/ts/on_deactivate/on_deactivate_spec.ts similarity index 100% rename from modules/@angular/examples/router/ts/on_deactivate/on_deactivate_spec.ts rename to modules/@angular/examples/router_deprecated/ts/on_deactivate/on_deactivate_spec.ts diff --git a/modules/@angular/examples/router/ts/reuse/index.html b/modules/@angular/examples/router_deprecated/ts/reuse/index.html similarity index 100% rename from modules/@angular/examples/router/ts/reuse/index.html rename to modules/@angular/examples/router_deprecated/ts/reuse/index.html diff --git a/modules/@angular/examples/router/ts/reuse/reuse_example.ts b/modules/@angular/examples/router_deprecated/ts/reuse/reuse_example.ts similarity index 100% rename from modules/@angular/examples/router/ts/reuse/reuse_example.ts rename to modules/@angular/examples/router_deprecated/ts/reuse/reuse_example.ts diff --git a/modules/@angular/examples/router/ts/reuse/reuse_spec.ts b/modules/@angular/examples/router_deprecated/ts/reuse/reuse_spec.ts similarity index 100% rename from modules/@angular/examples/router/ts/reuse/reuse_spec.ts rename to modules/@angular/examples/router_deprecated/ts/reuse/reuse_spec.ts diff --git a/modules/@angular/router/core_private.dart b/modules/@angular/router-deprecated/core_private.dart similarity index 100% rename from modules/@angular/router/core_private.dart rename to modules/@angular/router-deprecated/core_private.dart diff --git a/modules/@angular/router/core_private.ts b/modules/@angular/router-deprecated/core_private.ts similarity index 100% rename from modules/@angular/router/core_private.ts rename to modules/@angular/router-deprecated/core_private.ts diff --git a/modules/@angular/router-deprecated/index.ts b/modules/@angular/router-deprecated/index.ts new file mode 100644 index 0000000000..164ab508b5 --- /dev/null +++ b/modules/@angular/router-deprecated/index.ts @@ -0,0 +1 @@ +export * from './router'; diff --git a/modules/@angular/router/package.json b/modules/@angular/router-deprecated/package.json similarity index 100% rename from modules/@angular/router/package.json rename to modules/@angular/router-deprecated/package.json diff --git a/modules/@angular/router/rollup.config.js b/modules/@angular/router-deprecated/rollup.config.js similarity index 100% rename from modules/@angular/router/rollup.config.js rename to modules/@angular/router-deprecated/rollup.config.js diff --git a/modules/@angular/router/router.ts b/modules/@angular/router-deprecated/router.ts similarity index 100% rename from modules/@angular/router/router.ts rename to modules/@angular/router-deprecated/router.ts diff --git a/modules/@angular/router-deprecated/src/directives/router_link.ts b/modules/@angular/router-deprecated/src/directives/router_link.ts new file mode 100644 index 0000000000..a86c7fa3a8 --- /dev/null +++ b/modules/@angular/router-deprecated/src/directives/router_link.ts @@ -0,0 +1,83 @@ +import {Directive} from '@angular/core'; +import {Location} from '@angular/common'; +import {isString} from '../../src/facade/lang'; +import {Router} from '../router'; +import {Instruction} from '../instruction'; + +/** + * The RouterLink directive lets you link to specific parts of your app. + * + * Consider the following route configuration: + + * ``` + * @RouteConfig([ + * { path: '/user', component: UserCmp, as: 'User' } + * ]); + * class MyComp {} + * ``` + * + * When linking to this `User` route, you can write: + * + * ``` + * link to user component + * ``` + * + * RouterLink expects the value to be an array of route names, followed by the params + * for that level of routing. For instance `['/Team', {teamId: 1}, 'User', {userId: 2}]` + * means that we want to generate a link for the `Team` route with params `{teamId: 1}`, + * and with a child route `User` with params `{userId: 2}`. + * + * The first route name should be prepended with `/`, `./`, or `../`. + * If the route begins with `/`, the router will look up the route from the root of the app. + * If the route begins with `./`, the router will instead look in the current component's + * children for the route. And if the route begins with `../`, the router will look at the + * current component's parent. + */ +@Directive({ + selector: '[routerLink]', + inputs: ['routeParams: routerLink', 'target: target'], + host: { + '(click)': 'onClick()', + '[attr.href]': 'visibleHref', + '[class.router-link-active]': 'isRouteActive' + } +}) +export class RouterLink { + private _routeParams: any[]; + + // the url displayed on the anchor element. + visibleHref: string; + target: string; + + // the instruction passed to the router to navigate + private _navigationInstruction: Instruction; + + constructor(private _router: Router, private _location: Location) { + // we need to update the link whenever a route changes to account for aux routes + this._router.subscribe((_) => this._updateLink()); + } + + // because auxiliary links take existing primary and auxiliary routes into account, + // we need to update the link whenever params or other routes change. + private _updateLink(): void { + this._navigationInstruction = this._router.generate(this._routeParams); + var navigationHref = this._navigationInstruction.toLinkUrl(); + this.visibleHref = this._location.prepareExternalUrl(navigationHref); + } + + get isRouteActive(): boolean { return this._router.isRouteActive(this._navigationInstruction); } + + set routeParams(changes: any[]) { + this._routeParams = changes; + this._updateLink(); + } + + onClick(): boolean { + // If no target, or if target is _self, prevent default browser behavior + if (!isString(this.target) || this.target == '_self') { + this._router.navigateByInstruction(this._navigationInstruction); + return false; + } + return true; + } +} diff --git a/modules/@angular/router-deprecated/src/directives/router_outlet.ts b/modules/@angular/router-deprecated/src/directives/router_outlet.ts new file mode 100644 index 0000000000..f7279758fe --- /dev/null +++ b/modules/@angular/router-deprecated/src/directives/router_outlet.ts @@ -0,0 +1,176 @@ +import {PromiseWrapper, EventEmitter} from '../../src/facade/async'; +import {StringMapWrapper} from '../../src/facade/collection'; +import {isBlank, isPresent} from '../../src/facade/lang'; +import { + Directive, + Attribute, + DynamicComponentLoader, + ComponentRef, + ViewContainerRef, + provide, + ReflectiveInjector, + OnDestroy, + Output +} from '@angular/core'; +import * as routerMod from '../router'; +import {ComponentInstruction, RouteParams, RouteData} from '../instruction'; +import * as hookMod from '../lifecycle/lifecycle_annotations'; +import {hasLifecycleHook} from '../lifecycle/route_lifecycle_reflector'; +import {OnActivate, CanReuse, OnReuse, OnDeactivate, CanDeactivate} from '../interfaces'; + +let _resolveToTrue = PromiseWrapper.resolve(true); + +/** + * A router outlet is a placeholder that Angular dynamically fills based on the application's route. + * + * ## Use + * + * ``` + * + * ``` + */ +@Directive({selector: 'router-outlet'}) +export class RouterOutlet implements OnDestroy { + name: string = null; + private _componentRef: Promise> = null; + private _currentInstruction: ComponentInstruction = null; + + @Output('activate') public activateEvents = new EventEmitter(); + + constructor(private _viewContainerRef: ViewContainerRef, private _loader: DynamicComponentLoader, + private _parentRouter: routerMod.Router, @Attribute('name') nameAttr: string) { + if (isPresent(nameAttr)) { + this.name = nameAttr; + this._parentRouter.registerAuxOutlet(this); + } else { + this._parentRouter.registerPrimaryOutlet(this); + } + } + + /** + * Called by the Router to instantiate a new component during the commit phase of a navigation. + * This method in turn is responsible for calling the `routerOnActivate` hook of its child. + */ + activate(nextInstruction: ComponentInstruction): Promise { + var previousInstruction = this._currentInstruction; + this._currentInstruction = nextInstruction; + var componentType = nextInstruction.componentType; + var childRouter = this._parentRouter.childRouter(componentType); + + var providers = ReflectiveInjector.resolve([ + provide(RouteData, {useValue: nextInstruction.routeData}), + provide(RouteParams, {useValue: new RouteParams(nextInstruction.params)}), + provide(routerMod.Router, {useValue: childRouter}) + ]); + this._componentRef = + this._loader.loadNextToLocation(componentType, this._viewContainerRef, providers); + return this._componentRef.then((componentRef) => { + this.activateEvents.emit(componentRef.instance); + if (hasLifecycleHook(hookMod.routerOnActivate, componentType)) { + return this._componentRef.then( + (ref: ComponentRef) => + (ref.instance).routerOnActivate(nextInstruction, previousInstruction)); + } else { + return componentRef; + } + }); + } + + /** + * Called by the {@link Router} during the commit phase of a navigation when an outlet + * reuses a component between different routes. + * This method in turn is responsible for calling the `routerOnReuse` hook of its child. + */ + reuse(nextInstruction: ComponentInstruction): Promise { + var previousInstruction = this._currentInstruction; + this._currentInstruction = nextInstruction; + + // it's possible the component is removed before it can be reactivated (if nested withing + // another dynamically loaded component, for instance). In that case, we simply activate + // a new one. + if (isBlank(this._componentRef)) { + return this.activate(nextInstruction); + } else { + return PromiseWrapper.resolve( + hasLifecycleHook(hookMod.routerOnReuse, this._currentInstruction.componentType) ? + this._componentRef.then( + (ref: ComponentRef) => + (ref.instance).routerOnReuse(nextInstruction, previousInstruction)) : + true); + } + } + + /** + * Called by the {@link Router} when an outlet disposes of a component's contents. + * This method in turn is responsible for calling the `routerOnDeactivate` hook of its child. + */ + deactivate(nextInstruction: ComponentInstruction): Promise { + var next = _resolveToTrue; + if (isPresent(this._componentRef) && isPresent(this._currentInstruction) && + hasLifecycleHook(hookMod.routerOnDeactivate, this._currentInstruction.componentType)) { + next = this._componentRef.then( + (ref: ComponentRef) => + (ref.instance) + .routerOnDeactivate(nextInstruction, this._currentInstruction)); + } + return next.then((_) => { + if (isPresent(this._componentRef)) { + var onDispose = this._componentRef.then((ref: ComponentRef) => ref.destroy()); + this._componentRef = null; + return onDispose; + } + }); + } + + /** + * Called by the {@link Router} during recognition phase of a navigation. + * + * If this resolves to `false`, the given navigation is cancelled. + * + * This method delegates to the child component's `routerCanDeactivate` hook if it exists, + * and otherwise resolves to true. + */ + routerCanDeactivate(nextInstruction: ComponentInstruction): Promise { + if (isBlank(this._currentInstruction)) { + return _resolveToTrue; + } + if (hasLifecycleHook(hookMod.routerCanDeactivate, this._currentInstruction.componentType)) { + return this._componentRef.then( + (ref: ComponentRef) => + (ref.instance) + .routerCanDeactivate(nextInstruction, this._currentInstruction)); + } else { + return _resolveToTrue; + } + } + + /** + * Called by the {@link Router} during recognition phase of a navigation. + * + * If the new child component has a different Type than the existing child component, + * this will resolve to `false`. You can't reuse an old component when the new component + * is of a different Type. + * + * Otherwise, this method delegates to the child component's `routerCanReuse` hook if it exists, + * or resolves to true if the hook is not present. + */ + routerCanReuse(nextInstruction: ComponentInstruction): Promise { + var result; + + if (isBlank(this._currentInstruction) || + this._currentInstruction.componentType != nextInstruction.componentType) { + result = false; + } else if (hasLifecycleHook(hookMod.routerCanReuse, this._currentInstruction.componentType)) { + result = this._componentRef.then( + (ref: ComponentRef) => + (ref.instance).routerCanReuse(nextInstruction, this._currentInstruction)); + } else { + result = nextInstruction == this._currentInstruction || + (isPresent(nextInstruction.params) && isPresent(this._currentInstruction.params) && + StringMapWrapper.equals(nextInstruction.params, this._currentInstruction.params)); + } + return >PromiseWrapper.resolve(result); + } + + ngOnDestroy(): void { this._parentRouter.unregisterPrimaryOutlet(this); } +} diff --git a/modules/@angular/router/src/facade b/modules/@angular/router-deprecated/src/facade similarity index 100% rename from modules/@angular/router/src/facade rename to modules/@angular/router-deprecated/src/facade diff --git a/modules/@angular/router/src/instruction.ts b/modules/@angular/router-deprecated/src/instruction.ts similarity index 100% rename from modules/@angular/router/src/instruction.ts rename to modules/@angular/router-deprecated/src/instruction.ts diff --git a/modules/@angular/router-deprecated/src/interfaces.ts b/modules/@angular/router-deprecated/src/interfaces.ts new file mode 100644 index 0000000000..1298448a0f --- /dev/null +++ b/modules/@angular/router-deprecated/src/interfaces.ts @@ -0,0 +1,124 @@ +import {ComponentInstruction} from './instruction'; +import {global} from '../src/facade/lang'; + +// This is here only so that after TS transpilation the file is not empty. +// TODO(rado): find a better way to fix this, or remove if likely culprit +// https://github.com/systemjs/systemjs/issues/487 gets closed. +var __ignore_me = global; +var __make_dart_analyzer_happy: Promise = null; + +/** + * Defines route lifecycle method `routerOnActivate`, which is called by the router at the end of a + * successful route navigation. + * + * For a single component's navigation, only one of either {@link OnActivate} or {@link OnReuse} + * will be called depending on the result of {@link CanReuse}. + * + * The `routerOnActivate` hook is called with two {@link ComponentInstruction}s as parameters, the + * first + * representing the current route being navigated to, and the second parameter representing the + * previous route or `null`. + * + * If `routerOnActivate` returns a promise, the route change will wait until the promise settles to + * instantiate and activate child components. + * + * ### Example + * {@example router/ts/on_activate/on_activate_example.ts region='routerOnActivate'} + */ +export interface OnActivate { + routerOnActivate(nextInstruction: ComponentInstruction, + prevInstruction: ComponentInstruction): any | + Promise; +} + +/** + * Defines route lifecycle method `routerOnReuse`, which is called by the router at the end of a + * successful route navigation when {@link CanReuse} is implemented and returns or resolves to true. + * + * For a single component's navigation, only one of either {@link OnActivate} or {@link OnReuse} + * will be called, depending on the result of {@link CanReuse}. + * + * The `routerOnReuse` hook is called with two {@link ComponentInstruction}s as parameters, the + * first + * representing the current route being navigated to, and the second parameter representing the + * previous route or `null`. + * + * ### Example + * {@example router/ts/reuse/reuse_example.ts region='reuseCmp'} + */ +export interface OnReuse { + routerOnReuse(nextInstruction: ComponentInstruction, prevInstruction: ComponentInstruction): any | + Promise; +} + +/** + * Defines route lifecycle method `routerOnDeactivate`, which is called by the router before + * destroying + * a component as part of a route change. + * + * The `routerOnDeactivate` hook is called with two {@link ComponentInstruction}s as parameters, the + * first + * representing the current route being navigated to, and the second parameter representing the + * previous route. + * + * If `routerOnDeactivate` returns a promise, the route change will wait until the promise settles. + * + * ### Example + * {@example router/ts/on_deactivate/on_deactivate_example.ts region='routerOnDeactivate'} + */ +export interface OnDeactivate { + routerOnDeactivate(nextInstruction: ComponentInstruction, + prevInstruction: ComponentInstruction): any | + Promise; +} + +/** + * Defines route lifecycle method `routerCanReuse`, which is called by the router to determine + * whether a + * component should be reused across routes, or whether to destroy and instantiate a new component. + * + * The `routerCanReuse` hook is called with two {@link ComponentInstruction}s as parameters, the + * first + * representing the current route being navigated to, and the second parameter representing the + * previous route. + * + * If `routerCanReuse` returns or resolves to `true`, the component instance will be reused and the + * {@link OnDeactivate} hook will be run. If `routerCanReuse` returns or resolves to `false`, a new + * component will be instantiated, and the existing component will be deactivated and removed as + * part of the navigation. + * + * If `routerCanReuse` throws or rejects, the navigation will be cancelled. + * + * ### Example + * {@example router/ts/reuse/reuse_example.ts region='reuseCmp'} + */ +export interface CanReuse { + routerCanReuse(nextInstruction: ComponentInstruction, + prevInstruction: ComponentInstruction): boolean | + Promise; +} + +/** + * Defines route lifecycle method `routerCanDeactivate`, which is called by the router to determine + * if a component can be removed as part of a navigation. + * + * The `routerCanDeactivate` hook is called with two {@link ComponentInstruction}s as parameters, + * the + * first representing the current route being navigated to, and the second parameter + * representing the previous route. + * + * If `routerCanDeactivate` returns or resolves to `false`, the navigation is cancelled. If it + * returns or + * resolves to `true`, then the navigation continues, and the component will be deactivated + * (the {@link OnDeactivate} hook will be run) and removed. + * + * If `routerCanDeactivate` throws or rejects, the navigation is also cancelled. + * + * ### Example + * {@example router/ts/can_deactivate/can_deactivate_example.ts region='routerCanDeactivate'} + */ +export interface CanDeactivate { + routerCanDeactivate(nextInstruction: ComponentInstruction, + prevInstruction: ComponentInstruction): boolean | + Promise; +} diff --git a/modules/@angular/router/src/lifecycle/lifecycle_annotations.dart b/modules/@angular/router-deprecated/src/lifecycle/lifecycle_annotations.dart similarity index 100% rename from modules/@angular/router/src/lifecycle/lifecycle_annotations.dart rename to modules/@angular/router-deprecated/src/lifecycle/lifecycle_annotations.dart diff --git a/modules/@angular/router/src/lifecycle/lifecycle_annotations.ts b/modules/@angular/router-deprecated/src/lifecycle/lifecycle_annotations.ts similarity index 100% rename from modules/@angular/router/src/lifecycle/lifecycle_annotations.ts rename to modules/@angular/router-deprecated/src/lifecycle/lifecycle_annotations.ts diff --git a/modules/@angular/router/src/lifecycle/lifecycle_annotations_impl.ts b/modules/@angular/router-deprecated/src/lifecycle/lifecycle_annotations_impl.ts similarity index 100% rename from modules/@angular/router/src/lifecycle/lifecycle_annotations_impl.ts rename to modules/@angular/router-deprecated/src/lifecycle/lifecycle_annotations_impl.ts diff --git a/modules/@angular/router/src/lifecycle/route_lifecycle_reflector.dart b/modules/@angular/router-deprecated/src/lifecycle/route_lifecycle_reflector.dart similarity index 100% rename from modules/@angular/router/src/lifecycle/route_lifecycle_reflector.dart rename to modules/@angular/router-deprecated/src/lifecycle/route_lifecycle_reflector.dart diff --git a/modules/@angular/router/src/lifecycle/route_lifecycle_reflector.ts b/modules/@angular/router-deprecated/src/lifecycle/route_lifecycle_reflector.ts similarity index 100% rename from modules/@angular/router/src/lifecycle/route_lifecycle_reflector.ts rename to modules/@angular/router-deprecated/src/lifecycle/route_lifecycle_reflector.ts diff --git a/modules/@angular/router/src/package.json b/modules/@angular/router-deprecated/src/package.json similarity index 100% rename from modules/@angular/router/src/package.json rename to modules/@angular/router-deprecated/src/package.json diff --git a/modules/@angular/router/src/route_config/route_config_decorator.dart b/modules/@angular/router-deprecated/src/route_config/route_config_decorator.dart similarity index 100% rename from modules/@angular/router/src/route_config/route_config_decorator.dart rename to modules/@angular/router-deprecated/src/route_config/route_config_decorator.dart diff --git a/modules/@angular/router/src/route_config/route_config_decorator.ts b/modules/@angular/router-deprecated/src/route_config/route_config_decorator.ts similarity index 100% rename from modules/@angular/router/src/route_config/route_config_decorator.ts rename to modules/@angular/router-deprecated/src/route_config/route_config_decorator.ts diff --git a/modules/@angular/router/src/route_config/route_config_impl.ts b/modules/@angular/router-deprecated/src/route_config/route_config_impl.ts similarity index 100% rename from modules/@angular/router/src/route_config/route_config_impl.ts rename to modules/@angular/router-deprecated/src/route_config/route_config_impl.ts diff --git a/modules/@angular/router/src/route_config/route_config_normalizer.dart b/modules/@angular/router-deprecated/src/route_config/route_config_normalizer.dart similarity index 100% rename from modules/@angular/router/src/route_config/route_config_normalizer.dart rename to modules/@angular/router-deprecated/src/route_config/route_config_normalizer.dart diff --git a/modules/@angular/router/src/route_config/route_config_normalizer.ts b/modules/@angular/router-deprecated/src/route_config/route_config_normalizer.ts similarity index 100% rename from modules/@angular/router/src/route_config/route_config_normalizer.ts rename to modules/@angular/router-deprecated/src/route_config/route_config_normalizer.ts diff --git a/modules/@angular/router/src/route_definition.dart b/modules/@angular/router-deprecated/src/route_definition.dart similarity index 100% rename from modules/@angular/router/src/route_definition.dart rename to modules/@angular/router-deprecated/src/route_definition.dart diff --git a/modules/@angular/router/src/route_definition.ts b/modules/@angular/router-deprecated/src/route_definition.ts similarity index 100% rename from modules/@angular/router/src/route_definition.ts rename to modules/@angular/router-deprecated/src/route_definition.ts diff --git a/modules/@angular/router/src/route_registry.ts b/modules/@angular/router-deprecated/src/route_registry.ts similarity index 100% rename from modules/@angular/router/src/route_registry.ts rename to modules/@angular/router-deprecated/src/route_registry.ts diff --git a/modules/@angular/router-deprecated/src/router.ts b/modules/@angular/router-deprecated/src/router.ts new file mode 100644 index 0000000000..7587686a21 --- /dev/null +++ b/modules/@angular/router-deprecated/src/router.ts @@ -0,0 +1,578 @@ +import {PromiseWrapper, EventEmitter, ObservableWrapper} from '../src/facade/async'; +import {Map, StringMapWrapper} from '../src/facade/collection'; +import {isBlank, isPresent, Type} from '../src/facade/lang'; +import {BaseException} from '../src/facade/exceptions'; +import {Location} from '@angular/common'; +import {RouteRegistry, ROUTER_PRIMARY_COMPONENT} from './route_registry'; +import {ComponentInstruction, Instruction} from './instruction'; +import {RouterOutlet} from './directives/router_outlet'; +import {getCanActivateHook} from './lifecycle/route_lifecycle_reflector'; +import {RouteDefinition} from './route_config/route_config_impl'; +import {Injectable, Inject} from '@angular/core'; + +let _resolveToTrue = PromiseWrapper.resolve(true); +let _resolveToFalse = PromiseWrapper.resolve(false); + +/** + * The `Router` is responsible for mapping URLs to components. + * + * You can see the state of the router by inspecting the read-only field `router.navigating`. + * This may be useful for showing a spinner, for instance. + * + * ## Concepts + * + * Routers and component instances have a 1:1 correspondence. + * + * The router holds reference to a number of {@link RouterOutlet}. + * An outlet is a placeholder that the router dynamically fills in depending on the current URL. + * + * When the router navigates from a URL, it must first recognize it and serialize it into an + * `Instruction`. + * The router uses the `RouteRegistry` to get an `Instruction`. + */ +@Injectable() +export class Router { + navigating: boolean = false; + lastNavigationAttempt: string; + /** + * The current `Instruction` for the router + */ + public currentInstruction: Instruction = null; + + private _currentNavigation: Promise = _resolveToTrue; + private _outlet: RouterOutlet = null; + + private _auxRouters = new Map(); + private _childRouter: Router; + + private _subject: EventEmitter = new EventEmitter(); + + + constructor(public registry: RouteRegistry, public parent: Router, public hostComponent: any, + public root?: Router) {} + + /** + * Constructs a child router. You probably don't need to use this unless you're writing a reusable + * component. + */ + childRouter(hostComponent: any): Router { + return this._childRouter = new ChildRouter(this, hostComponent); + } + + + /** + * Constructs a child router. You probably don't need to use this unless you're writing a reusable + * component. + */ + auxRouter(hostComponent: any): Router { return new ChildRouter(this, hostComponent); } + + /** + * Register an outlet to be notified of primary route changes. + * + * You probably don't need to use this unless you're writing a reusable component. + */ + registerPrimaryOutlet(outlet: RouterOutlet): Promise { + if (isPresent(outlet.name)) { + throw new BaseException(`registerPrimaryOutlet expects to be called with an unnamed outlet.`); + } + + if (isPresent(this._outlet)) { + throw new BaseException(`Primary outlet is already registered.`); + } + + this._outlet = outlet; + if (isPresent(this.currentInstruction)) { + return this.commit(this.currentInstruction, false); + } + return _resolveToTrue; + } + + /** + * Unregister an outlet (because it was destroyed, etc). + * + * You probably don't need to use this unless you're writing a custom outlet implementation. + */ + unregisterPrimaryOutlet(outlet: RouterOutlet): void { + if (isPresent(outlet.name)) { + throw new BaseException(`registerPrimaryOutlet expects to be called with an unnamed outlet.`); + } + this._outlet = null; + } + + + /** + * Register an outlet to notified of auxiliary route changes. + * + * You probably don't need to use this unless you're writing a reusable component. + */ + registerAuxOutlet(outlet: RouterOutlet): Promise { + var outletName = outlet.name; + if (isBlank(outletName)) { + throw new BaseException(`registerAuxOutlet expects to be called with an outlet with a name.`); + } + + var router = this.auxRouter(this.hostComponent); + + this._auxRouters.set(outletName, router); + router._outlet = outlet; + + var auxInstruction; + if (isPresent(this.currentInstruction) && + isPresent(auxInstruction = this.currentInstruction.auxInstruction[outletName])) { + return router.commit(auxInstruction); + } + return _resolveToTrue; + } + + + /** + * Given an instruction, returns `true` if the instruction is currently active, + * otherwise `false`. + */ + isRouteActive(instruction: Instruction): boolean { + var router: Router = this; + + if (isBlank(this.currentInstruction)) { + return false; + } + + // `instruction` corresponds to the root router + while (isPresent(router.parent) && isPresent(instruction.child)) { + router = router.parent; + instruction = instruction.child; + } + + if (isBlank(instruction.component) || isBlank(this.currentInstruction.component) || + this.currentInstruction.component.routeName != instruction.component.routeName) { + return false; + } + + let paramEquals = true; + + if (isPresent(this.currentInstruction.component.params)) { + StringMapWrapper.forEach(instruction.component.params, (value, key) => { + if (this.currentInstruction.component.params[key] !== value) { + paramEquals = false; + } + }); + } + + return paramEquals; + } + + + /** + * Dynamically update the routing configuration and trigger a navigation. + * + * ### Usage + * + * ``` + * router.config([ + * { 'path': '/', 'component': IndexComp }, + * { 'path': '/user/:id', 'component': UserComp }, + * ]); + * ``` + */ + config(definitions: RouteDefinition[]): Promise { + definitions.forEach( + (routeDefinition) => { this.registry.config(this.hostComponent, routeDefinition); }); + return this.renavigate(); + } + + + /** + * Navigate based on the provided Route Link DSL. It's preferred to navigate with this method + * over `navigateByUrl`. + * + * ### Usage + * + * This method takes an array representing the Route Link DSL: + * ``` + * ['./MyCmp', {param: 3}] + * ``` + * See the {@link RouterLink} directive for more. + */ + navigate(linkParams: any[]): Promise { + var instruction = this.generate(linkParams); + return this.navigateByInstruction(instruction, false); + } + + + /** + * Navigate to a URL. Returns a promise that resolves when navigation is complete. + * It's preferred to navigate with `navigate` instead of this method, since URLs are more brittle. + * + * If the given URL begins with a `/`, router will navigate absolutely. + * If the given URL does not begin with `/`, the router will navigate relative to this component. + */ + navigateByUrl(url: string, _skipLocationChange: boolean = false): Promise { + return this._currentNavigation = this._currentNavigation.then((_) => { + this.lastNavigationAttempt = url; + this._startNavigating(); + return this._afterPromiseFinishNavigating(this.recognize(url).then((instruction) => { + if (isBlank(instruction)) { + return false; + } + return this._navigate(instruction, _skipLocationChange); + })); + }); + } + + + /** + * Navigate via the provided instruction. Returns a promise that resolves when navigation is + * complete. + */ + navigateByInstruction(instruction: Instruction, + _skipLocationChange: boolean = false): Promise { + if (isBlank(instruction)) { + return _resolveToFalse; + } + return this._currentNavigation = this._currentNavigation.then((_) => { + this._startNavigating(); + return this._afterPromiseFinishNavigating(this._navigate(instruction, _skipLocationChange)); + }); + } + + /** @internal */ + _settleInstruction(instruction: Instruction): Promise { + return instruction.resolveComponent().then((_) => { + var unsettledInstructions: Array> = []; + + if (isPresent(instruction.component)) { + instruction.component.reuse = false; + } + + if (isPresent(instruction.child)) { + unsettledInstructions.push(this._settleInstruction(instruction.child)); + } + + StringMapWrapper.forEach(instruction.auxInstruction, (instruction: Instruction, _) => { + unsettledInstructions.push(this._settleInstruction(instruction)); + }); + return PromiseWrapper.all(unsettledInstructions); + }); + } + + /** @internal */ + _navigate(instruction: Instruction, _skipLocationChange: boolean): Promise { + return this._settleInstruction(instruction) + .then((_) => this._routerCanReuse(instruction)) + .then((_) => this._canActivate(instruction)) + .then((result: boolean) => { + if (!result) { + return false; + } + return this._routerCanDeactivate(instruction) + .then((result: boolean) => { + if (result) { + return this.commit(instruction, _skipLocationChange) + .then((_) => { + this._emitNavigationFinish(instruction.toRootUrl()); + return true; + }); + } + }); + }); + } + + private _emitNavigationFinish(url): void { ObservableWrapper.callEmit(this._subject, url); } + /** @internal */ + _emitNavigationFail(url): void { ObservableWrapper.callError(this._subject, url); } + + private _afterPromiseFinishNavigating(promise: Promise): Promise { + return PromiseWrapper.catchError(promise.then((_) => this._finishNavigating()), (err) => { + this._finishNavigating(); + throw err; + }); + } + + /* + * Recursively set reuse flags + */ + /** @internal */ + _routerCanReuse(instruction: Instruction): Promise { + if (isBlank(this._outlet)) { + return _resolveToFalse; + } + if (isBlank(instruction.component)) { + return _resolveToTrue; + } + return this._outlet.routerCanReuse(instruction.component) + .then((result) => { + instruction.component.reuse = result; + if (result && isPresent(this._childRouter) && isPresent(instruction.child)) { + return this._childRouter._routerCanReuse(instruction.child); + } + }); + } + + private _canActivate(nextInstruction: Instruction): Promise { + return canActivateOne(nextInstruction, this.currentInstruction); + } + + private _routerCanDeactivate(instruction: Instruction): Promise { + if (isBlank(this._outlet)) { + return _resolveToTrue; + } + var next: Promise; + var childInstruction: Instruction = null; + var reuse: boolean = false; + var componentInstruction: ComponentInstruction = null; + if (isPresent(instruction)) { + childInstruction = instruction.child; + componentInstruction = instruction.component; + reuse = isBlank(instruction.component) || instruction.component.reuse; + } + if (reuse) { + next = _resolveToTrue; + } else { + next = this._outlet.routerCanDeactivate(componentInstruction); + } + // TODO: aux route lifecycle hooks + return next.then((result): boolean | Promise => { + if (result == false) { + return false; + } + if (isPresent(this._childRouter)) { + // TODO: ideally, this closure would map to async-await in Dart. + // For now, casting to any to suppress an error. + return this._childRouter._routerCanDeactivate(childInstruction); + } + return true; + }); + } + + /** + * Updates this router and all descendant routers according to the given instruction + */ + commit(instruction: Instruction, _skipLocationChange: boolean = false): Promise { + this.currentInstruction = instruction; + + var next: Promise = _resolveToTrue; + if (isPresent(this._outlet) && isPresent(instruction.component)) { + var componentInstruction = instruction.component; + if (componentInstruction.reuse) { + next = this._outlet.reuse(componentInstruction); + } else { + next = + this.deactivate(instruction).then((_) => this._outlet.activate(componentInstruction)); + } + if (isPresent(instruction.child)) { + next = next.then((_) => { + if (isPresent(this._childRouter)) { + return this._childRouter.commit(instruction.child); + } + }); + } + } + + var promises: Promise[] = []; + this._auxRouters.forEach((router, name) => { + if (isPresent(instruction.auxInstruction[name])) { + promises.push(router.commit(instruction.auxInstruction[name])); + } + }); + + return next.then((_) => PromiseWrapper.all(promises)); + } + + + /** @internal */ + _startNavigating(): void { this.navigating = true; } + + /** @internal */ + _finishNavigating(): void { this.navigating = false; } + + + /** + * Subscribe to URL updates from the router + */ + subscribe(onNext: (value: any) => void, onError?: (value: any) => void): Object { + return ObservableWrapper.subscribe(this._subject, onNext, onError); + } + + + /** + * Removes the contents of this router's outlet and all descendant outlets + */ + deactivate(instruction: Instruction): Promise { + var childInstruction: Instruction = null; + var componentInstruction: ComponentInstruction = null; + if (isPresent(instruction)) { + childInstruction = instruction.child; + componentInstruction = instruction.component; + } + var next: Promise = _resolveToTrue; + if (isPresent(this._childRouter)) { + next = this._childRouter.deactivate(childInstruction); + } + if (isPresent(this._outlet)) { + next = next.then((_) => this._outlet.deactivate(componentInstruction)); + } + + // TODO: handle aux routes + + return next; + } + + + /** + * Given a URL, returns an instruction representing the component graph + */ + recognize(url: string): Promise { + var ancestorComponents = this._getAncestorInstructions(); + return this.registry.recognize(url, ancestorComponents); + } + + private _getAncestorInstructions(): Instruction[] { + var ancestorInstructions: Instruction[] = [this.currentInstruction]; + var ancestorRouter: Router = this; + while (isPresent(ancestorRouter = ancestorRouter.parent)) { + ancestorInstructions.unshift(ancestorRouter.currentInstruction); + } + return ancestorInstructions; + } + + + /** + * Navigates to either the last URL successfully navigated to, or the last URL requested if the + * router has yet to successfully navigate. + */ + renavigate(): Promise { + if (isBlank(this.lastNavigationAttempt)) { + return this._currentNavigation; + } + return this.navigateByUrl(this.lastNavigationAttempt); + } + + + /** + * Generate an `Instruction` based on the provided Route Link DSL. + */ + generate(linkParams: any[]): Instruction { + var ancestorInstructions = this._getAncestorInstructions(); + return this.registry.generate(linkParams, ancestorInstructions); + } +} + +@Injectable() +export class RootRouter extends Router { + /** @internal */ + _location: Location; + /** @internal */ + _locationSub: Object; + + constructor(registry: RouteRegistry, location: Location, + @Inject(ROUTER_PRIMARY_COMPONENT) primaryComponent: Type) { + super(registry, null, primaryComponent); + this.root = this; + this._location = location; + this._locationSub = this._location.subscribe((change) => { + // we call recognize ourselves + this.recognize(change['url']) + .then((instruction) => { + if (isPresent(instruction)) { + this.navigateByInstruction(instruction, isPresent(change['pop'])) + .then((_) => { + // this is a popstate event; no need to change the URL + if (isPresent(change['pop']) && change['type'] != 'hashchange') { + return; + } + var emitPath = instruction.toUrlPath(); + var emitQuery = instruction.toUrlQuery(); + if (emitPath.length > 0 && emitPath[0] != '/') { + emitPath = '/' + emitPath; + } + + // We've opted to use pushstate and popState APIs regardless of whether you + // an app uses HashLocationStrategy or PathLocationStrategy. + // However, apps that are migrating might have hash links that operate outside + // angular to which routing must respond. + // Therefore we know that all hashchange events occur outside Angular. + // To support these cases where we respond to hashchanges and redirect as a + // result, we need to replace the top item on the stack. + if (change['type'] == 'hashchange') { + if (instruction.toRootUrl() != this._location.path()) { + this._location.replaceState(emitPath, emitQuery); + } + } else { + this._location.go(emitPath, emitQuery); + } + }); + } else { + this._emitNavigationFail(change['url']); + } + }); + }); + + this.registry.configFromComponent(primaryComponent); + this.navigateByUrl(location.path()); + } + + commit(instruction: Instruction, _skipLocationChange: boolean = false): Promise { + var emitPath = instruction.toUrlPath(); + var emitQuery = instruction.toUrlQuery(); + if (emitPath.length > 0 && emitPath[0] != '/') { + emitPath = '/' + emitPath; + } + var promise = super.commit(instruction); + if (!_skipLocationChange) { + promise = promise.then((_) => { this._location.go(emitPath, emitQuery); }); + } + return promise; + } + + dispose(): void { + if (isPresent(this._locationSub)) { + ObservableWrapper.dispose(this._locationSub); + this._locationSub = null; + } + } +} + +class ChildRouter extends Router { + constructor(parent: Router, hostComponent) { + super(parent.registry, parent, hostComponent, parent.root); + this.parent = parent; + } + + + navigateByUrl(url: string, _skipLocationChange: boolean = false): Promise { + // Delegate navigation to the root router + return this.parent.navigateByUrl(url, _skipLocationChange); + } + + navigateByInstruction(instruction: Instruction, + _skipLocationChange: boolean = false): Promise { + // Delegate navigation to the root router + return this.parent.navigateByInstruction(instruction, _skipLocationChange); + } +} + + +function canActivateOne(nextInstruction: Instruction, + prevInstruction: Instruction): Promise { + var next = _resolveToTrue; + if (isBlank(nextInstruction.component)) { + return next; + } + if (isPresent(nextInstruction.child)) { + next = canActivateOne(nextInstruction.child, + isPresent(prevInstruction) ? prevInstruction.child : null); + } + return next.then((result: boolean): boolean => { + if (result == false) { + return false; + } + if (nextInstruction.component.reuse) { + return true; + } + var hook = getCanActivateHook(nextInstruction.component.componentType); + if (isPresent(hook)) { + return hook(nextInstruction.component, + isPresent(prevInstruction) ? prevInstruction.component : null); + } + return true; + }); +} diff --git a/modules/@angular/router-deprecated/src/router_providers.ts b/modules/@angular/router-deprecated/src/router_providers.ts new file mode 100644 index 0000000000..baddb26ed0 --- /dev/null +++ b/modules/@angular/router-deprecated/src/router_providers.ts @@ -0,0 +1,41 @@ +import {ROUTER_PROVIDERS_COMMON} from './router_providers_common'; +import {Provider} from '@angular/core'; +import {BrowserPlatformLocation} from '@angular/platform-browser'; +import {PlatformLocation} from '@angular/common'; + +/** + * A list of {@link Provider}s. To use the router, you must add this to your application. + * + * ### Example ([live demo](http://plnkr.co/edit/iRUP8B5OUbxCWQ3AcIDm)) + * + * ``` + * import {Component} from '@angular/core'; + * import { + * ROUTER_DIRECTIVES, + * ROUTER_PROVIDERS, + * RouteConfig + * } from '@angular/router'; + * + * @Component({directives: [ROUTER_DIRECTIVES]}) + * @RouteConfig([ + * {...}, + * ]) + * class AppCmp { + * // ... + * } + * + * bootstrap(AppCmp, [ROUTER_PROVIDERS]); + * ``` + */ +export const ROUTER_PROVIDERS: any[] = /*@ts2dart_const*/[ + ROUTER_PROVIDERS_COMMON, + /*@ts2dart_const*/ ( + /* @ts2dart_Provider */ {provide: PlatformLocation, useClass: BrowserPlatformLocation}), +]; + +/** + * Use {@link ROUTER_PROVIDERS} instead. + * + * @deprecated + */ +export const ROUTER_BINDINGS = /*@ts2dart_const*/ ROUTER_PROVIDERS; diff --git a/modules/@angular/router-deprecated/src/router_providers_common.ts b/modules/@angular/router-deprecated/src/router_providers_common.ts new file mode 100644 index 0000000000..a9042754e1 --- /dev/null +++ b/modules/@angular/router-deprecated/src/router_providers_common.ts @@ -0,0 +1,39 @@ +import {ApplicationRef, Provider} from '@angular/core'; +import {LocationStrategy, PathLocationStrategy, Location} from '@angular/common'; +import {Router, RootRouter} from './router'; +import {RouteRegistry, ROUTER_PRIMARY_COMPONENT} from './route_registry'; +import {Type} from '../src/facade/lang'; +import {BaseException} from '../src/facade/exceptions'; + +/** + * The Platform agnostic ROUTER PROVIDERS + */ +export const ROUTER_PROVIDERS_COMMON: any[] = /*@ts2dart_const*/[ + RouteRegistry, + /* @ts2dart_Provider */ {provide: LocationStrategy, useClass: PathLocationStrategy}, + Location, + { + provide: Router, + useFactory: routerFactory, + deps: [RouteRegistry, Location, ROUTER_PRIMARY_COMPONENT, ApplicationRef] + }, + { + provide: ROUTER_PRIMARY_COMPONENT, + useFactory: routerPrimaryComponentFactory, + deps: /*@ts2dart_const*/ ([ApplicationRef]) + } +]; + +function routerFactory(registry: RouteRegistry, location: Location, primaryComponent: Type, + appRef: ApplicationRef): RootRouter { + var rootRouter = new RootRouter(registry, location, primaryComponent); + appRef.registerDisposeListener(() => rootRouter.dispose()); + return rootRouter; +} + +function routerPrimaryComponentFactory(app: ApplicationRef): Type { + if (app.componentTypes.length == 0) { + throw new BaseException("Bootstrap at least one component before injecting Router."); + } + return app.componentTypes[0]; +} diff --git a/modules/@angular/router/src/rules/route_handlers/async_route_handler.ts b/modules/@angular/router-deprecated/src/rules/route_handlers/async_route_handler.ts similarity index 100% rename from modules/@angular/router/src/rules/route_handlers/async_route_handler.ts rename to modules/@angular/router-deprecated/src/rules/route_handlers/async_route_handler.ts diff --git a/modules/@angular/router/src/rules/route_handlers/route_handler.ts b/modules/@angular/router-deprecated/src/rules/route_handlers/route_handler.ts similarity index 100% rename from modules/@angular/router/src/rules/route_handlers/route_handler.ts rename to modules/@angular/router-deprecated/src/rules/route_handlers/route_handler.ts diff --git a/modules/@angular/router/src/rules/route_handlers/sync_route_handler.ts b/modules/@angular/router-deprecated/src/rules/route_handlers/sync_route_handler.ts similarity index 100% rename from modules/@angular/router/src/rules/route_handlers/sync_route_handler.ts rename to modules/@angular/router-deprecated/src/rules/route_handlers/sync_route_handler.ts diff --git a/modules/@angular/router/src/rules/route_paths/param_route_path.ts b/modules/@angular/router-deprecated/src/rules/route_paths/param_route_path.ts similarity index 100% rename from modules/@angular/router/src/rules/route_paths/param_route_path.ts rename to modules/@angular/router-deprecated/src/rules/route_paths/param_route_path.ts diff --git a/modules/@angular/router/src/rules/route_paths/regex_route_path.ts b/modules/@angular/router-deprecated/src/rules/route_paths/regex_route_path.ts similarity index 100% rename from modules/@angular/router/src/rules/route_paths/regex_route_path.ts rename to modules/@angular/router-deprecated/src/rules/route_paths/regex_route_path.ts diff --git a/modules/@angular/router/src/rules/route_paths/route_path.ts b/modules/@angular/router-deprecated/src/rules/route_paths/route_path.ts similarity index 100% rename from modules/@angular/router/src/rules/route_paths/route_path.ts rename to modules/@angular/router-deprecated/src/rules/route_paths/route_path.ts diff --git a/modules/@angular/router/src/rules/rule_set.ts b/modules/@angular/router-deprecated/src/rules/rule_set.ts similarity index 100% rename from modules/@angular/router/src/rules/rule_set.ts rename to modules/@angular/router-deprecated/src/rules/rule_set.ts diff --git a/modules/@angular/router/src/rules/rules.ts b/modules/@angular/router-deprecated/src/rules/rules.ts similarity index 100% rename from modules/@angular/router/src/rules/rules.ts rename to modules/@angular/router-deprecated/src/rules/rules.ts diff --git a/modules/@angular/router/src/url_parser.ts b/modules/@angular/router-deprecated/src/url_parser.ts similarity index 100% rename from modules/@angular/router/src/url_parser.ts rename to modules/@angular/router-deprecated/src/url_parser.ts diff --git a/modules/@angular/router/src/utils.ts b/modules/@angular/router-deprecated/src/utils.ts similarity index 100% rename from modules/@angular/router/src/utils.ts rename to modules/@angular/router-deprecated/src/utils.ts diff --git a/modules/@angular/router/test/directives/router_link_spec.ts b/modules/@angular/router-deprecated/test/directives/router_link_spec.ts similarity index 100% rename from modules/@angular/router/test/directives/router_link_spec.ts rename to modules/@angular/router-deprecated/test/directives/router_link_spec.ts diff --git a/modules/@angular/router/test/integration/README.md b/modules/@angular/router-deprecated/test/integration/README.md similarity index 100% rename from modules/@angular/router/test/integration/README.md rename to modules/@angular/router-deprecated/test/integration/README.md diff --git a/modules/@angular/router/test/integration/async_route_spec.ts b/modules/@angular/router-deprecated/test/integration/async_route_spec.ts similarity index 100% rename from modules/@angular/router/test/integration/async_route_spec.ts rename to modules/@angular/router-deprecated/test/integration/async_route_spec.ts diff --git a/modules/@angular/router/test/integration/auxiliary_route_spec.ts b/modules/@angular/router-deprecated/test/integration/auxiliary_route_spec.ts similarity index 100% rename from modules/@angular/router/test/integration/auxiliary_route_spec.ts rename to modules/@angular/router-deprecated/test/integration/auxiliary_route_spec.ts diff --git a/modules/@angular/router/test/integration/bootstrap_spec.ts b/modules/@angular/router-deprecated/test/integration/bootstrap_spec.ts similarity index 100% rename from modules/@angular/router/test/integration/bootstrap_spec.ts rename to modules/@angular/router-deprecated/test/integration/bootstrap_spec.ts diff --git a/modules/@angular/router/test/integration/impl/async_route_spec_impl.ts b/modules/@angular/router-deprecated/test/integration/impl/async_route_spec_impl.ts similarity index 100% rename from modules/@angular/router/test/integration/impl/async_route_spec_impl.ts rename to modules/@angular/router-deprecated/test/integration/impl/async_route_spec_impl.ts diff --git a/modules/@angular/router/test/integration/impl/aux_route_spec_impl.ts b/modules/@angular/router-deprecated/test/integration/impl/aux_route_spec_impl.ts similarity index 100% rename from modules/@angular/router/test/integration/impl/aux_route_spec_impl.ts rename to modules/@angular/router-deprecated/test/integration/impl/aux_route_spec_impl.ts diff --git a/modules/@angular/router/test/integration/impl/fixture_components.ts b/modules/@angular/router-deprecated/test/integration/impl/fixture_components.ts similarity index 100% rename from modules/@angular/router/test/integration/impl/fixture_components.ts rename to modules/@angular/router-deprecated/test/integration/impl/fixture_components.ts diff --git a/modules/@angular/router/test/integration/impl/sync_route_spec_impl.ts b/modules/@angular/router-deprecated/test/integration/impl/sync_route_spec_impl.ts similarity index 100% rename from modules/@angular/router/test/integration/impl/sync_route_spec_impl.ts rename to modules/@angular/router-deprecated/test/integration/impl/sync_route_spec_impl.ts diff --git a/modules/@angular/router/test/integration/lifecycle_hook_spec.ts b/modules/@angular/router-deprecated/test/integration/lifecycle_hook_spec.ts similarity index 100% rename from modules/@angular/router/test/integration/lifecycle_hook_spec.ts rename to modules/@angular/router-deprecated/test/integration/lifecycle_hook_spec.ts diff --git a/modules/@angular/router/test/integration/navigation_spec.ts b/modules/@angular/router-deprecated/test/integration/navigation_spec.ts similarity index 100% rename from modules/@angular/router/test/integration/navigation_spec.ts rename to modules/@angular/router-deprecated/test/integration/navigation_spec.ts diff --git a/modules/@angular/router/test/integration/redirect_route_spec.ts b/modules/@angular/router-deprecated/test/integration/redirect_route_spec.ts similarity index 100% rename from modules/@angular/router/test/integration/redirect_route_spec.ts rename to modules/@angular/router-deprecated/test/integration/redirect_route_spec.ts diff --git a/modules/@angular/router/test/integration/router_link_spec.ts b/modules/@angular/router-deprecated/test/integration/router_link_spec.ts similarity index 100% rename from modules/@angular/router/test/integration/router_link_spec.ts rename to modules/@angular/router-deprecated/test/integration/router_link_spec.ts diff --git a/modules/@angular/router/test/integration/sync_route_spec.ts b/modules/@angular/router-deprecated/test/integration/sync_route_spec.ts similarity index 100% rename from modules/@angular/router/test/integration/sync_route_spec.ts rename to modules/@angular/router-deprecated/test/integration/sync_route_spec.ts diff --git a/modules/@angular/router/test/integration/util.ts b/modules/@angular/router-deprecated/test/integration/util.ts similarity index 100% rename from modules/@angular/router/test/integration/util.ts rename to modules/@angular/router-deprecated/test/integration/util.ts diff --git a/modules/@angular/router/test/location/hash_location_strategy_spec.ts b/modules/@angular/router-deprecated/test/location/hash_location_strategy_spec.ts similarity index 100% rename from modules/@angular/router/test/location/hash_location_strategy_spec.ts rename to modules/@angular/router-deprecated/test/location/hash_location_strategy_spec.ts diff --git a/modules/@angular/router/test/location/location_spec.ts b/modules/@angular/router-deprecated/test/location/location_spec.ts similarity index 100% rename from modules/@angular/router/test/location/location_spec.ts rename to modules/@angular/router-deprecated/test/location/location_spec.ts diff --git a/modules/@angular/router/test/location/path_location_strategy_spec.ts b/modules/@angular/router-deprecated/test/location/path_location_strategy_spec.ts similarity index 100% rename from modules/@angular/router/test/location/path_location_strategy_spec.ts rename to modules/@angular/router-deprecated/test/location/path_location_strategy_spec.ts diff --git a/modules/@angular/router/test/route_config/route_config_spec.dart b/modules/@angular/router-deprecated/test/route_config/route_config_spec.dart similarity index 100% rename from modules/@angular/router/test/route_config/route_config_spec.dart rename to modules/@angular/router-deprecated/test/route_config/route_config_spec.dart diff --git a/modules/@angular/router/test/route_config/route_config_spec.ts b/modules/@angular/router-deprecated/test/route_config/route_config_spec.ts similarity index 100% rename from modules/@angular/router/test/route_config/route_config_spec.ts rename to modules/@angular/router-deprecated/test/route_config/route_config_spec.ts diff --git a/modules/@angular/router/test/route_registry_spec.ts b/modules/@angular/router-deprecated/test/route_registry_spec.ts similarity index 100% rename from modules/@angular/router/test/route_registry_spec.ts rename to modules/@angular/router-deprecated/test/route_registry_spec.ts diff --git a/modules/@angular/router/test/router_spec.ts b/modules/@angular/router-deprecated/test/router_spec.ts similarity index 100% rename from modules/@angular/router/test/router_spec.ts rename to modules/@angular/router-deprecated/test/router_spec.ts diff --git a/modules/@angular/router/test/rules/route_paths/param_route_path_spec.ts b/modules/@angular/router-deprecated/test/rules/route_paths/param_route_path_spec.ts similarity index 100% rename from modules/@angular/router/test/rules/route_paths/param_route_path_spec.ts rename to modules/@angular/router-deprecated/test/rules/route_paths/param_route_path_spec.ts diff --git a/modules/@angular/router/test/rules/route_paths/regex_route_param_spec.ts b/modules/@angular/router-deprecated/test/rules/route_paths/regex_route_param_spec.ts similarity index 100% rename from modules/@angular/router/test/rules/route_paths/regex_route_param_spec.ts rename to modules/@angular/router-deprecated/test/rules/route_paths/regex_route_param_spec.ts diff --git a/modules/@angular/router/test/rules/rule_set_spec.ts b/modules/@angular/router-deprecated/test/rules/rule_set_spec.ts similarity index 100% rename from modules/@angular/router/test/rules/rule_set_spec.ts rename to modules/@angular/router-deprecated/test/rules/rule_set_spec.ts diff --git a/modules/@angular/router/test/spies.dart b/modules/@angular/router-deprecated/test/spies.dart similarity index 100% rename from modules/@angular/router/test/spies.dart rename to modules/@angular/router-deprecated/test/spies.dart diff --git a/modules/@angular/router/test/spies.ts b/modules/@angular/router-deprecated/test/spies.ts similarity index 100% rename from modules/@angular/router/test/spies.ts rename to modules/@angular/router-deprecated/test/spies.ts diff --git a/modules/@angular/router/test/url_parser_spec.ts b/modules/@angular/router-deprecated/test/url_parser_spec.ts similarity index 100% rename from modules/@angular/router/test/url_parser_spec.ts rename to modules/@angular/router-deprecated/test/url_parser_spec.ts diff --git a/modules/@angular/router/testing.ts b/modules/@angular/router-deprecated/testing.ts similarity index 100% rename from modules/@angular/router/testing.ts rename to modules/@angular/router-deprecated/testing.ts diff --git a/modules/@angular/router/tsconfig-es2015.json b/modules/@angular/router-deprecated/tsconfig-es2015.json similarity index 100% rename from modules/@angular/router/tsconfig-es2015.json rename to modules/@angular/router-deprecated/tsconfig-es2015.json diff --git a/modules/@angular/router/tsconfig.json b/modules/@angular/router-deprecated/tsconfig.json similarity index 100% rename from modules/@angular/router/tsconfig.json rename to modules/@angular/router-deprecated/tsconfig.json diff --git a/modules/@angular/router/index.ts b/modules/@angular/router/index.ts index 164ab508b5..d7ea77a688 100644 --- a/modules/@angular/router/index.ts +++ b/modules/@angular/router/index.ts @@ -1 +1,21 @@ -export * from './router'; +/** + * @module + * @description + * Alternative implementation of the router. Experimental. + */ + +export {Router, RouterOutletMap} from './src/alt_router/router'; +export {RouteSegment, UrlSegment, Tree, UrlTree, RouteTree} from './src/alt_router/segments'; +export {Routes} from './src/alt_router/metadata/decorators'; +export {Route} from './src/alt_router/metadata/metadata'; +export { + RouterUrlSerializer, + DefaultRouterUrlSerializer +} from './src/alt_router/router_url_serializer'; +export {OnActivate, CanDeactivate} from './src/alt_router/interfaces'; +export {ROUTER_PROVIDERS} from './src/alt_router/router_providers'; + +import {RouterOutlet} from './src/alt_router/directives/router_outlet'; +import {RouterLink} from './src/alt_router/directives/router_link'; + +export const ROUTER_DIRECTIVES: any[] = /*@ts2dart_const*/[RouterOutlet, RouterLink]; diff --git a/modules/angular2/src/alt_router/constants.ts b/modules/@angular/router/src/constants.ts similarity index 100% rename from modules/angular2/src/alt_router/constants.ts rename to modules/@angular/router/src/constants.ts diff --git a/modules/angular2/src/alt_router/core_private.ts b/modules/@angular/router/src/core_private.ts similarity index 100% rename from modules/angular2/src/alt_router/core_private.ts rename to modules/@angular/router/src/core_private.ts diff --git a/modules/@angular/router/src/directives/router_link.ts b/modules/@angular/router/src/directives/router_link.ts index a86c7fa3a8..8c662d3e38 100644 --- a/modules/@angular/router/src/directives/router_link.ts +++ b/modules/@angular/router/src/directives/router_link.ts @@ -1,83 +1,62 @@ -import {Directive} from '@angular/core'; -import {Location} from '@angular/common'; -import {isString} from '../../src/facade/lang'; -import {Router} from '../router'; -import {Instruction} from '../instruction'; +import { + ResolvedReflectiveProvider, + Directive, + DynamicComponentLoader, + ViewContainerRef, + Attribute, + ComponentRef, + ComponentFactory, + ReflectiveInjector, + OnInit, + HostListener, + HostBinding, + Input, + OnDestroy, + Optional +} from '@angular/core'; +import {RouterOutletMap, Router} from '../router'; +import {RouteSegment, UrlSegment, Tree} from '../segments'; +import {isString, isPresent} from '@angular/facade/src/lang'; +import {ObservableWrapper} from '@angular/facade/src/async'; -/** - * The RouterLink directive lets you link to specific parts of your app. - * - * Consider the following route configuration: +@Directive({selector: '[routerLink]'}) +export class RouterLink implements OnDestroy { + @Input() target: string; + private _changes: any[] = []; + private _subscription: any; - * ``` - * @RouteConfig([ - * { path: '/user', component: UserCmp, as: 'User' } - * ]); - * class MyComp {} - * ``` - * - * When linking to this `User` route, you can write: - * - * ``` - * link to user component - * ``` - * - * RouterLink expects the value to be an array of route names, followed by the params - * for that level of routing. For instance `['/Team', {teamId: 1}, 'User', {userId: 2}]` - * means that we want to generate a link for the `Team` route with params `{teamId: 1}`, - * and with a child route `User` with params `{userId: 2}`. - * - * The first route name should be prepended with `/`, `./`, or `../`. - * If the route begins with `/`, the router will look up the route from the root of the app. - * If the route begins with `./`, the router will instead look in the current component's - * children for the route. And if the route begins with `../`, the router will look at the - * current component's parent. - */ -@Directive({ - selector: '[routerLink]', - inputs: ['routeParams: routerLink', 'target: target'], - host: { - '(click)': 'onClick()', - '[attr.href]': 'visibleHref', - '[class.router-link-active]': 'isRouteActive' - } -}) -export class RouterLink { - private _routeParams: any[]; + @HostBinding() private href: string; + @HostBinding('class.router-link-active') private isActive: boolean = false; - // the url displayed on the anchor element. - visibleHref: string; - target: string; - - // the instruction passed to the router to navigate - private _navigationInstruction: Instruction; - - constructor(private _router: Router, private _location: Location) { - // we need to update the link whenever a route changes to account for aux routes - this._router.subscribe((_) => this._updateLink()); + constructor(@Optional() private _routeSegment: RouteSegment, private _router: Router) { + this._subscription = + ObservableWrapper.subscribe(_router.changes, (_) => { this._updateTargetUrlAndHref(); }); } - // because auxiliary links take existing primary and auxiliary routes into account, - // we need to update the link whenever params or other routes change. - private _updateLink(): void { - this._navigationInstruction = this._router.generate(this._routeParams); - var navigationHref = this._navigationInstruction.toLinkUrl(); - this.visibleHref = this._location.prepareExternalUrl(navigationHref); - } - - get isRouteActive(): boolean { return this._router.isRouteActive(this._navigationInstruction); } - - set routeParams(changes: any[]) { - this._routeParams = changes; - this._updateLink(); + ngOnDestroy() { ObservableWrapper.dispose(this._subscription); } + + @Input() + set routerLink(data: any[]) { + this._changes = data; + this._updateTargetUrlAndHref(); } + @HostListener("click") onClick(): boolean { - // If no target, or if target is _self, prevent default browser behavior if (!isString(this.target) || this.target == '_self') { - this._router.navigateByInstruction(this._navigationInstruction); + this._router.navigate(this._changes, this._routeSegment); return false; } return true; } + + private _updateTargetUrlAndHref(): void { + let tree = this._router.createUrlTree(this._changes, this._routeSegment); + if (isPresent(tree)) { + this.href = this._router.serializeUrl(tree); + this.isActive = this._router.urlTree.contains(tree); + } else { + this.isActive = false; + } + } } diff --git a/modules/@angular/router/src/directives/router_outlet.ts b/modules/@angular/router/src/directives/router_outlet.ts index f7279758fe..fbdb041d95 100644 --- a/modules/@angular/router/src/directives/router_outlet.ts +++ b/modules/@angular/router/src/directives/router_outlet.ts @@ -1,176 +1,42 @@ -import {PromiseWrapper, EventEmitter} from '../../src/facade/async'; -import {StringMapWrapper} from '../../src/facade/collection'; -import {isBlank, isPresent} from '../../src/facade/lang'; import { + ResolvedReflectiveProvider, Directive, - Attribute, DynamicComponentLoader, - ComponentRef, ViewContainerRef, - provide, + Attribute, + ComponentRef, + ComponentFactory, ReflectiveInjector, - OnDestroy, - Output + OnInit } from '@angular/core'; -import * as routerMod from '../router'; -import {ComponentInstruction, RouteParams, RouteData} from '../instruction'; -import * as hookMod from '../lifecycle/lifecycle_annotations'; -import {hasLifecycleHook} from '../lifecycle/route_lifecycle_reflector'; -import {OnActivate, CanReuse, OnReuse, OnDeactivate, CanDeactivate} from '../interfaces'; +import {RouterOutletMap} from '../router'; +import {DEFAULT_OUTLET_NAME} from '../constants'; +import {isPresent, isBlank} from '@angular/facade/src/lang'; -let _resolveToTrue = PromiseWrapper.resolve(true); - -/** - * A router outlet is a placeholder that Angular dynamically fills based on the application's route. - * - * ## Use - * - * ``` - * - * ``` - */ @Directive({selector: 'router-outlet'}) -export class RouterOutlet implements OnDestroy { - name: string = null; - private _componentRef: Promise> = null; - private _currentInstruction: ComponentInstruction = null; +export class RouterOutlet { + private _loaded: ComponentRef; + public outletMap: RouterOutletMap; - @Output('activate') public activateEvents = new EventEmitter(); - - constructor(private _viewContainerRef: ViewContainerRef, private _loader: DynamicComponentLoader, - private _parentRouter: routerMod.Router, @Attribute('name') nameAttr: string) { - if (isPresent(nameAttr)) { - this.name = nameAttr; - this._parentRouter.registerAuxOutlet(this); - } else { - this._parentRouter.registerPrimaryOutlet(this); - } + constructor(parentOutletMap: RouterOutletMap, private _location: ViewContainerRef, + @Attribute('name') name: string) { + parentOutletMap.registerOutlet(isBlank(name) ? DEFAULT_OUTLET_NAME : name, this); } - /** - * Called by the Router to instantiate a new component during the commit phase of a navigation. - * This method in turn is responsible for calling the `routerOnActivate` hook of its child. - */ - activate(nextInstruction: ComponentInstruction): Promise { - var previousInstruction = this._currentInstruction; - this._currentInstruction = nextInstruction; - var componentType = nextInstruction.componentType; - var childRouter = this._parentRouter.childRouter(componentType); - - var providers = ReflectiveInjector.resolve([ - provide(RouteData, {useValue: nextInstruction.routeData}), - provide(RouteParams, {useValue: new RouteParams(nextInstruction.params)}), - provide(routerMod.Router, {useValue: childRouter}) - ]); - this._componentRef = - this._loader.loadNextToLocation(componentType, this._viewContainerRef, providers); - return this._componentRef.then((componentRef) => { - this.activateEvents.emit(componentRef.instance); - if (hasLifecycleHook(hookMod.routerOnActivate, componentType)) { - return this._componentRef.then( - (ref: ComponentRef) => - (ref.instance).routerOnActivate(nextInstruction, previousInstruction)); - } else { - return componentRef; - } - }); + unload(): void { + this._loaded.destroy(); + this._loaded = null; } - /** - * Called by the {@link Router} during the commit phase of a navigation when an outlet - * reuses a component between different routes. - * This method in turn is responsible for calling the `routerOnReuse` hook of its child. - */ - reuse(nextInstruction: ComponentInstruction): Promise { - var previousInstruction = this._currentInstruction; - this._currentInstruction = nextInstruction; + get loadedComponent(): Object { return isPresent(this._loaded) ? this._loaded.instance : null; } - // it's possible the component is removed before it can be reactivated (if nested withing - // another dynamically loaded component, for instance). In that case, we simply activate - // a new one. - if (isBlank(this._componentRef)) { - return this.activate(nextInstruction); - } else { - return PromiseWrapper.resolve( - hasLifecycleHook(hookMod.routerOnReuse, this._currentInstruction.componentType) ? - this._componentRef.then( - (ref: ComponentRef) => - (ref.instance).routerOnReuse(nextInstruction, previousInstruction)) : - true); - } + get isLoaded(): boolean { return isPresent(this._loaded); } + + load(factory: ComponentFactory, providers: ResolvedReflectiveProvider[], + outletMap: RouterOutletMap): ComponentRef { + this.outletMap = outletMap; + let inj = ReflectiveInjector.fromResolvedProviders(providers, this._location.parentInjector); + this._loaded = this._location.createComponent(factory, this._location.length, inj, []); + return this._loaded; } - - /** - * Called by the {@link Router} when an outlet disposes of a component's contents. - * This method in turn is responsible for calling the `routerOnDeactivate` hook of its child. - */ - deactivate(nextInstruction: ComponentInstruction): Promise { - var next = _resolveToTrue; - if (isPresent(this._componentRef) && isPresent(this._currentInstruction) && - hasLifecycleHook(hookMod.routerOnDeactivate, this._currentInstruction.componentType)) { - next = this._componentRef.then( - (ref: ComponentRef) => - (ref.instance) - .routerOnDeactivate(nextInstruction, this._currentInstruction)); - } - return next.then((_) => { - if (isPresent(this._componentRef)) { - var onDispose = this._componentRef.then((ref: ComponentRef) => ref.destroy()); - this._componentRef = null; - return onDispose; - } - }); - } - - /** - * Called by the {@link Router} during recognition phase of a navigation. - * - * If this resolves to `false`, the given navigation is cancelled. - * - * This method delegates to the child component's `routerCanDeactivate` hook if it exists, - * and otherwise resolves to true. - */ - routerCanDeactivate(nextInstruction: ComponentInstruction): Promise { - if (isBlank(this._currentInstruction)) { - return _resolveToTrue; - } - if (hasLifecycleHook(hookMod.routerCanDeactivate, this._currentInstruction.componentType)) { - return this._componentRef.then( - (ref: ComponentRef) => - (ref.instance) - .routerCanDeactivate(nextInstruction, this._currentInstruction)); - } else { - return _resolveToTrue; - } - } - - /** - * Called by the {@link Router} during recognition phase of a navigation. - * - * If the new child component has a different Type than the existing child component, - * this will resolve to `false`. You can't reuse an old component when the new component - * is of a different Type. - * - * Otherwise, this method delegates to the child component's `routerCanReuse` hook if it exists, - * or resolves to true if the hook is not present. - */ - routerCanReuse(nextInstruction: ComponentInstruction): Promise { - var result; - - if (isBlank(this._currentInstruction) || - this._currentInstruction.componentType != nextInstruction.componentType) { - result = false; - } else if (hasLifecycleHook(hookMod.routerCanReuse, this._currentInstruction.componentType)) { - result = this._componentRef.then( - (ref: ComponentRef) => - (ref.instance).routerCanReuse(nextInstruction, this._currentInstruction)); - } else { - result = nextInstruction == this._currentInstruction || - (isPresent(nextInstruction.params) && isPresent(this._currentInstruction.params) && - StringMapWrapper.equals(nextInstruction.params, this._currentInstruction.params)); - } - return >PromiseWrapper.resolve(result); - } - - ngOnDestroy(): void { this._parentRouter.unregisterPrimaryOutlet(this); } } diff --git a/modules/@angular/router/src/interfaces.ts b/modules/@angular/router/src/interfaces.ts index 1298448a0f..c09b38df5f 100644 --- a/modules/@angular/router/src/interfaces.ts +++ b/modules/@angular/router/src/interfaces.ts @@ -1,124 +1,10 @@ -import {ComponentInstruction} from './instruction'; -import {global} from '../src/facade/lang'; +import {RouteSegment, Tree, RouteTree} from './segments'; -// This is here only so that after TS transpilation the file is not empty. -// TODO(rado): find a better way to fix this, or remove if likely culprit -// https://github.com/systemjs/systemjs/issues/487 gets closed. -var __ignore_me = global; -var __make_dart_analyzer_happy: Promise = null; - -/** - * Defines route lifecycle method `routerOnActivate`, which is called by the router at the end of a - * successful route navigation. - * - * For a single component's navigation, only one of either {@link OnActivate} or {@link OnReuse} - * will be called depending on the result of {@link CanReuse}. - * - * The `routerOnActivate` hook is called with two {@link ComponentInstruction}s as parameters, the - * first - * representing the current route being navigated to, and the second parameter representing the - * previous route or `null`. - * - * If `routerOnActivate` returns a promise, the route change will wait until the promise settles to - * instantiate and activate child components. - * - * ### Example - * {@example router/ts/on_activate/on_activate_example.ts region='routerOnActivate'} - */ export interface OnActivate { - routerOnActivate(nextInstruction: ComponentInstruction, - prevInstruction: ComponentInstruction): any | - Promise; + routerOnActivate(curr: RouteSegment, prev?: RouteSegment, currTree?: RouteTree, + prevTree?: RouteTree): void; } -/** - * Defines route lifecycle method `routerOnReuse`, which is called by the router at the end of a - * successful route navigation when {@link CanReuse} is implemented and returns or resolves to true. - * - * For a single component's navigation, only one of either {@link OnActivate} or {@link OnReuse} - * will be called, depending on the result of {@link CanReuse}. - * - * The `routerOnReuse` hook is called with two {@link ComponentInstruction}s as parameters, the - * first - * representing the current route being navigated to, and the second parameter representing the - * previous route or `null`. - * - * ### Example - * {@example router/ts/reuse/reuse_example.ts region='reuseCmp'} - */ -export interface OnReuse { - routerOnReuse(nextInstruction: ComponentInstruction, prevInstruction: ComponentInstruction): any | - Promise; -} - -/** - * Defines route lifecycle method `routerOnDeactivate`, which is called by the router before - * destroying - * a component as part of a route change. - * - * The `routerOnDeactivate` hook is called with two {@link ComponentInstruction}s as parameters, the - * first - * representing the current route being navigated to, and the second parameter representing the - * previous route. - * - * If `routerOnDeactivate` returns a promise, the route change will wait until the promise settles. - * - * ### Example - * {@example router/ts/on_deactivate/on_deactivate_example.ts region='routerOnDeactivate'} - */ -export interface OnDeactivate { - routerOnDeactivate(nextInstruction: ComponentInstruction, - prevInstruction: ComponentInstruction): any | - Promise; -} - -/** - * Defines route lifecycle method `routerCanReuse`, which is called by the router to determine - * whether a - * component should be reused across routes, or whether to destroy and instantiate a new component. - * - * The `routerCanReuse` hook is called with two {@link ComponentInstruction}s as parameters, the - * first - * representing the current route being navigated to, and the second parameter representing the - * previous route. - * - * If `routerCanReuse` returns or resolves to `true`, the component instance will be reused and the - * {@link OnDeactivate} hook will be run. If `routerCanReuse` returns or resolves to `false`, a new - * component will be instantiated, and the existing component will be deactivated and removed as - * part of the navigation. - * - * If `routerCanReuse` throws or rejects, the navigation will be cancelled. - * - * ### Example - * {@example router/ts/reuse/reuse_example.ts region='reuseCmp'} - */ -export interface CanReuse { - routerCanReuse(nextInstruction: ComponentInstruction, - prevInstruction: ComponentInstruction): boolean | - Promise; -} - -/** - * Defines route lifecycle method `routerCanDeactivate`, which is called by the router to determine - * if a component can be removed as part of a navigation. - * - * The `routerCanDeactivate` hook is called with two {@link ComponentInstruction}s as parameters, - * the - * first representing the current route being navigated to, and the second parameter - * representing the previous route. - * - * If `routerCanDeactivate` returns or resolves to `false`, the navigation is cancelled. If it - * returns or - * resolves to `true`, then the navigation continues, and the component will be deactivated - * (the {@link OnDeactivate} hook will be run) and removed. - * - * If `routerCanDeactivate` throws or rejects, the navigation is also cancelled. - * - * ### Example - * {@example router/ts/can_deactivate/can_deactivate_example.ts region='routerCanDeactivate'} - */ export interface CanDeactivate { - routerCanDeactivate(nextInstruction: ComponentInstruction, - prevInstruction: ComponentInstruction): boolean | - Promise; -} + routerCanDeactivate(currTree?: RouteTree, futureTree?: RouteTree): Promise; +} \ No newline at end of file diff --git a/modules/angular2/src/alt_router/lifecycle_reflector.dart b/modules/@angular/router/src/lifecycle_reflector.dart similarity index 100% rename from modules/angular2/src/alt_router/lifecycle_reflector.dart rename to modules/@angular/router/src/lifecycle_reflector.dart diff --git a/modules/angular2/src/alt_router/lifecycle_reflector.ts b/modules/@angular/router/src/lifecycle_reflector.ts similarity index 100% rename from modules/angular2/src/alt_router/lifecycle_reflector.ts rename to modules/@angular/router/src/lifecycle_reflector.ts diff --git a/modules/angular2/src/alt_router/link.ts b/modules/@angular/router/src/link.ts similarity index 100% rename from modules/angular2/src/alt_router/link.ts rename to modules/@angular/router/src/link.ts diff --git a/modules/angular2/src/alt_router/metadata/decorators.dart b/modules/@angular/router/src/metadata/decorators.dart similarity index 100% rename from modules/angular2/src/alt_router/metadata/decorators.dart rename to modules/@angular/router/src/metadata/decorators.dart diff --git a/modules/angular2/src/alt_router/metadata/decorators.ts b/modules/@angular/router/src/metadata/decorators.ts similarity index 100% rename from modules/angular2/src/alt_router/metadata/decorators.ts rename to modules/@angular/router/src/metadata/decorators.ts diff --git a/modules/angular2/src/alt_router/metadata/metadata.ts b/modules/@angular/router/src/metadata/metadata.ts similarity index 100% rename from modules/angular2/src/alt_router/metadata/metadata.ts rename to modules/@angular/router/src/metadata/metadata.ts diff --git a/modules/angular2/src/alt_router/recognize.ts b/modules/@angular/router/src/recognize.ts similarity index 100% rename from modules/angular2/src/alt_router/recognize.ts rename to modules/@angular/router/src/recognize.ts diff --git a/modules/@angular/router/src/router.ts b/modules/@angular/router/src/router.ts index 7587686a21..2c5bc6751e 100644 --- a/modules/@angular/router/src/router.ts +++ b/modules/@angular/router/src/router.ts @@ -1,578 +1,226 @@ -import {PromiseWrapper, EventEmitter, ObservableWrapper} from '../src/facade/async'; -import {Map, StringMapWrapper} from '../src/facade/collection'; -import {isBlank, isPresent, Type} from '../src/facade/lang'; -import {BaseException} from '../src/facade/exceptions'; -import {Location} from '@angular/common'; -import {RouteRegistry, ROUTER_PRIMARY_COMPONENT} from './route_registry'; -import {ComponentInstruction, Instruction} from './instruction'; +import {OnInit, provide, ReflectiveInjector, ComponentResolver} from '@angular/core'; import {RouterOutlet} from './directives/router_outlet'; -import {getCanActivateHook} from './lifecycle/route_lifecycle_reflector'; -import {RouteDefinition} from './route_config/route_config_impl'; -import {Injectable, Inject} from '@angular/core'; +import {Type, isBlank, isPresent} from '@angular/facade/src/lang'; +import {ListWrapper} from '@angular/facade/src/collection'; +import { + EventEmitter, + Observable, + PromiseWrapper, + ObservableWrapper +} from '@angular/facade/src/async'; +import {StringMapWrapper} from '@angular/facade/src/collection'; +import {BaseException} from '@angular/core'; +import {RouterUrlSerializer} from './router_url_serializer'; +import {CanDeactivate} from './interfaces'; +import {recognize} from './recognize'; +import {Location} from '@angular/common'; +import {link} from './link'; -let _resolveToTrue = PromiseWrapper.resolve(true); -let _resolveToFalse = PromiseWrapper.resolve(false); +import { + equalSegments, + routeSegmentComponentFactory, + RouteSegment, + UrlTree, + RouteTree, + rootNode, + TreeNode, + UrlSegment, + serializeRouteSegmentTree +} from './segments'; +import {hasLifecycleHook} from './lifecycle_reflector'; +import {DEFAULT_OUTLET_NAME} from './constants'; + +export class RouterOutletMap { + /** @internal */ + _outlets: {[name: string]: RouterOutlet} = {}; + registerOutlet(name: string, outlet: RouterOutlet): void { this._outlets[name] = outlet; } +} -/** - * The `Router` is responsible for mapping URLs to components. - * - * You can see the state of the router by inspecting the read-only field `router.navigating`. - * This may be useful for showing a spinner, for instance. - * - * ## Concepts - * - * Routers and component instances have a 1:1 correspondence. - * - * The router holds reference to a number of {@link RouterOutlet}. - * An outlet is a placeholder that the router dynamically fills in depending on the current URL. - * - * When the router navigates from a URL, it must first recognize it and serialize it into an - * `Instruction`. - * The router uses the `RouteRegistry` to get an `Instruction`. - */ -@Injectable() export class Router { - navigating: boolean = false; - lastNavigationAttempt: string; - /** - * The current `Instruction` for the router - */ - public currentInstruction: Instruction = null; + private _prevTree: RouteTree; + private _urlTree: UrlTree; + private _locationSubscription: any; + private _changes: EventEmitter = new EventEmitter(); - private _currentNavigation: Promise = _resolveToTrue; - private _outlet: RouterOutlet = null; - - private _auxRouters = new Map(); - private _childRouter: Router; - - private _subject: EventEmitter = new EventEmitter(); - - - constructor(public registry: RouteRegistry, public parent: Router, public hostComponent: any, - public root?: Router) {} - - /** - * Constructs a child router. You probably don't need to use this unless you're writing a reusable - * component. - */ - childRouter(hostComponent: any): Router { - return this._childRouter = new ChildRouter(this, hostComponent); + constructor(private _rootComponent: Object, private _rootComponentType: Type, + private _componentResolver: ComponentResolver, + private _urlSerializer: RouterUrlSerializer, + private _routerOutletMap: RouterOutletMap, private _location: Location) { + this._prevTree = this._createInitialTree(); + this._setUpLocationChangeListener(); + this.navigateByUrl(this._location.path()); } + get urlTree(): UrlTree { return this._urlTree; } - /** - * Constructs a child router. You probably don't need to use this unless you're writing a reusable - * component. - */ - auxRouter(hostComponent: any): Router { return new ChildRouter(this, hostComponent); } - - /** - * Register an outlet to be notified of primary route changes. - * - * You probably don't need to use this unless you're writing a reusable component. - */ - registerPrimaryOutlet(outlet: RouterOutlet): Promise { - if (isPresent(outlet.name)) { - throw new BaseException(`registerPrimaryOutlet expects to be called with an unnamed outlet.`); - } - - if (isPresent(this._outlet)) { - throw new BaseException(`Primary outlet is already registered.`); - } - - this._outlet = outlet; - if (isPresent(this.currentInstruction)) { - return this.commit(this.currentInstruction, false); - } - return _resolveToTrue; + navigateByUrl(url: string): Promise { + return this._navigate(this._urlSerializer.parse(url)); } - /** - * Unregister an outlet (because it was destroyed, etc). - * - * You probably don't need to use this unless you're writing a custom outlet implementation. - */ - unregisterPrimaryOutlet(outlet: RouterOutlet): void { - if (isPresent(outlet.name)) { - throw new BaseException(`registerPrimaryOutlet expects to be called with an unnamed outlet.`); - } - this._outlet = null; + navigate(changes: any[], segment?: RouteSegment): Promise { + return this._navigate(this.createUrlTree(changes, segment)); } + dispose(): void { ObservableWrapper.dispose(this._locationSubscription); } - /** - * Register an outlet to notified of auxiliary route changes. - * - * You probably don't need to use this unless you're writing a reusable component. - */ - registerAuxOutlet(outlet: RouterOutlet): Promise { - var outletName = outlet.name; - if (isBlank(outletName)) { - throw new BaseException(`registerAuxOutlet expects to be called with an outlet with a name.`); - } - - var router = this.auxRouter(this.hostComponent); - - this._auxRouters.set(outletName, router); - router._outlet = outlet; - - var auxInstruction; - if (isPresent(this.currentInstruction) && - isPresent(auxInstruction = this.currentInstruction.auxInstruction[outletName])) { - return router.commit(auxInstruction); - } - return _resolveToTrue; + private _createInitialTree(): RouteTree { + let root = new RouteSegment([new UrlSegment("", null, null)], null, DEFAULT_OUTLET_NAME, + this._rootComponentType, null); + return new RouteTree(new TreeNode(root, [])); } - - /** - * Given an instruction, returns `true` if the instruction is currently active, - * otherwise `false`. - */ - isRouteActive(instruction: Instruction): boolean { - var router: Router = this; - - if (isBlank(this.currentInstruction)) { - return false; - } - - // `instruction` corresponds to the root router - while (isPresent(router.parent) && isPresent(instruction.child)) { - router = router.parent; - instruction = instruction.child; - } - - if (isBlank(instruction.component) || isBlank(this.currentInstruction.component) || - this.currentInstruction.component.routeName != instruction.component.routeName) { - return false; - } - - let paramEquals = true; - - if (isPresent(this.currentInstruction.component.params)) { - StringMapWrapper.forEach(instruction.component.params, (value, key) => { - if (this.currentInstruction.component.params[key] !== value) { - paramEquals = false; - } - }); - } - - return paramEquals; + private _setUpLocationChangeListener(): void { + this._locationSubscription = this._location.subscribe( + (change) => { this._navigate(this._urlSerializer.parse(change['url'])); }); } - - /** - * Dynamically update the routing configuration and trigger a navigation. - * - * ### Usage - * - * ``` - * router.config([ - * { 'path': '/', 'component': IndexComp }, - * { 'path': '/user/:id', 'component': UserComp }, - * ]); - * ``` - */ - config(definitions: RouteDefinition[]): Promise { - definitions.forEach( - (routeDefinition) => { this.registry.config(this.hostComponent, routeDefinition); }); - return this.renavigate(); - } - - - /** - * Navigate based on the provided Route Link DSL. It's preferred to navigate with this method - * over `navigateByUrl`. - * - * ### Usage - * - * This method takes an array representing the Route Link DSL: - * ``` - * ['./MyCmp', {param: 3}] - * ``` - * See the {@link RouterLink} directive for more. - */ - navigate(linkParams: any[]): Promise { - var instruction = this.generate(linkParams); - return this.navigateByInstruction(instruction, false); - } - - - /** - * Navigate to a URL. Returns a promise that resolves when navigation is complete. - * It's preferred to navigate with `navigate` instead of this method, since URLs are more brittle. - * - * If the given URL begins with a `/`, router will navigate absolutely. - * If the given URL does not begin with `/`, the router will navigate relative to this component. - */ - navigateByUrl(url: string, _skipLocationChange: boolean = false): Promise { - return this._currentNavigation = this._currentNavigation.then((_) => { - this.lastNavigationAttempt = url; - this._startNavigating(); - return this._afterPromiseFinishNavigating(this.recognize(url).then((instruction) => { - if (isBlank(instruction)) { - return false; - } - return this._navigate(instruction, _skipLocationChange); - })); - }); - } - - - /** - * Navigate via the provided instruction. Returns a promise that resolves when navigation is - * complete. - */ - navigateByInstruction(instruction: Instruction, - _skipLocationChange: boolean = false): Promise { - if (isBlank(instruction)) { - return _resolveToFalse; - } - return this._currentNavigation = this._currentNavigation.then((_) => { - this._startNavigating(); - return this._afterPromiseFinishNavigating(this._navigate(instruction, _skipLocationChange)); - }); - } - - /** @internal */ - _settleInstruction(instruction: Instruction): Promise { - return instruction.resolveComponent().then((_) => { - var unsettledInstructions: Array> = []; - - if (isPresent(instruction.component)) { - instruction.component.reuse = false; - } - - if (isPresent(instruction.child)) { - unsettledInstructions.push(this._settleInstruction(instruction.child)); - } - - StringMapWrapper.forEach(instruction.auxInstruction, (instruction: Instruction, _) => { - unsettledInstructions.push(this._settleInstruction(instruction)); - }); - return PromiseWrapper.all(unsettledInstructions); - }); - } - - /** @internal */ - _navigate(instruction: Instruction, _skipLocationChange: boolean): Promise { - return this._settleInstruction(instruction) - .then((_) => this._routerCanReuse(instruction)) - .then((_) => this._canActivate(instruction)) - .then((result: boolean) => { - if (!result) { - return false; - } - return this._routerCanDeactivate(instruction) - .then((result: boolean) => { - if (result) { - return this.commit(instruction, _skipLocationChange) - .then((_) => { - this._emitNavigationFinish(instruction.toRootUrl()); - return true; - }); + private _navigate(url: UrlTree): Promise { + this._urlTree = url; + return recognize(this._componentResolver, this._rootComponentType, url) + .then(currTree => { + return new _LoadSegments(currTree, this._prevTree) + .load(this._routerOutletMap, this._rootComponent) + .then(updated => { + if (updated) { + this._prevTree = currTree; + this._location.go(this._urlSerializer.serialize(this._urlTree)); + this._changes.emit(null); } }); }); } - private _emitNavigationFinish(url): void { ObservableWrapper.callEmit(this._subject, url); } - /** @internal */ - _emitNavigationFail(url): void { ObservableWrapper.callError(this._subject, url); } - - private _afterPromiseFinishNavigating(promise: Promise): Promise { - return PromiseWrapper.catchError(promise.then((_) => this._finishNavigating()), (err) => { - this._finishNavigating(); - throw err; - }); - } - - /* - * Recursively set reuse flags - */ - /** @internal */ - _routerCanReuse(instruction: Instruction): Promise { - if (isBlank(this._outlet)) { - return _resolveToFalse; - } - if (isBlank(instruction.component)) { - return _resolveToTrue; - } - return this._outlet.routerCanReuse(instruction.component) - .then((result) => { - instruction.component.reuse = result; - if (result && isPresent(this._childRouter) && isPresent(instruction.child)) { - return this._childRouter._routerCanReuse(instruction.child); - } - }); - } - - private _canActivate(nextInstruction: Instruction): Promise { - return canActivateOne(nextInstruction, this.currentInstruction); - } - - private _routerCanDeactivate(instruction: Instruction): Promise { - if (isBlank(this._outlet)) { - return _resolveToTrue; - } - var next: Promise; - var childInstruction: Instruction = null; - var reuse: boolean = false; - var componentInstruction: ComponentInstruction = null; - if (isPresent(instruction)) { - childInstruction = instruction.child; - componentInstruction = instruction.component; - reuse = isBlank(instruction.component) || instruction.component.reuse; - } - if (reuse) { - next = _resolveToTrue; + createUrlTree(changes: any[], segment?: RouteSegment): UrlTree { + if (isPresent(this._prevTree)) { + let s = isPresent(segment) ? segment : this._prevTree.root; + return link(s, this._prevTree, this.urlTree, changes); } else { - next = this._outlet.routerCanDeactivate(componentInstruction); + return null; } - // TODO: aux route lifecycle hooks - return next.then((result): boolean | Promise => { - if (result == false) { - return false; - } - if (isPresent(this._childRouter)) { - // TODO: ideally, this closure would map to async-await in Dart. - // For now, casting to any to suppress an error. - return this._childRouter._routerCanDeactivate(childInstruction); - } - return true; - }); } - /** - * Updates this router and all descendant routers according to the given instruction - */ - commit(instruction: Instruction, _skipLocationChange: boolean = false): Promise { - this.currentInstruction = instruction; + serializeUrl(url: UrlTree): string { return this._urlSerializer.serialize(url); } - var next: Promise = _resolveToTrue; - if (isPresent(this._outlet) && isPresent(instruction.component)) { - var componentInstruction = instruction.component; - if (componentInstruction.reuse) { - next = this._outlet.reuse(componentInstruction); - } else { - next = - this.deactivate(instruction).then((_) => this._outlet.activate(componentInstruction)); - } - if (isPresent(instruction.child)) { - next = next.then((_) => { - if (isPresent(this._childRouter)) { - return this._childRouter.commit(instruction.child); + get changes(): Observable { return this._changes; } + + get routeTree(): RouteTree { return this._prevTree; } +} + + +class _LoadSegments { + private deactivations: Object[][] = []; + private performMutation: boolean = true; + + constructor(private currTree: RouteTree, private prevTree: RouteTree) {} + + load(parentOutletMap: RouterOutletMap, rootComponent: Object): Promise { + let prevRoot = isPresent(this.prevTree) ? rootNode(this.prevTree) : null; + let currRoot = rootNode(this.currTree); + + return this.canDeactivate(currRoot, prevRoot, parentOutletMap, rootComponent) + .then(res => { + this.performMutation = true; + if (res) { + this.loadChildSegments(currRoot, prevRoot, parentOutletMap, [rootComponent]); } + return res; }); - } - } + } - var promises: Promise[] = []; - this._auxRouters.forEach((router, name) => { - if (isPresent(instruction.auxInstruction[name])) { - promises.push(router.commit(instruction.auxInstruction[name])); - } + private canDeactivate(currRoot: TreeNode, prevRoot: TreeNode, + outletMap: RouterOutletMap, rootComponent: Object): Promise { + this.performMutation = false; + this.loadChildSegments(currRoot, prevRoot, outletMap, [rootComponent]); + + let allPaths = PromiseWrapper.all(this.deactivations.map(r => this.checkCanDeactivatePath(r))); + return allPaths.then((values: boolean[]) => values.filter(v => v).length === values.length); + } + + private checkCanDeactivatePath(path: Object[]): Promise { + let curr = PromiseWrapper.resolve(true); + for (let p of ListWrapper.reversed(path)) { + curr = curr.then(_ => { + if (hasLifecycleHook("routerCanDeactivate", p)) { + return (p).routerCanDeactivate(this.prevTree, this.currTree); + } else { + return _; + } + }); + } + return curr; + } + + private loadChildSegments(currNode: TreeNode, prevNode: TreeNode, + outletMap: RouterOutletMap, components: Object[]): void { + let prevChildren = isPresent(prevNode) ? + prevNode.children.reduce( + (m, c) => { + m[c.value.outlet] = c; + return m; + }, + {}) : + {}; + + currNode.children.forEach(c => { + this.loadSegments(c, prevChildren[c.value.outlet], outletMap, components); + StringMapWrapper.delete(prevChildren, c.value.outlet); }); - return next.then((_) => PromiseWrapper.all(promises)); + StringMapWrapper.forEach(prevChildren, + (v, k) => this.unloadOutlet(outletMap._outlets[k], components)); } + loadSegments(currNode: TreeNode, prevNode: TreeNode, + parentOutletMap: RouterOutletMap, components: Object[]): void { + let curr = currNode.value; + let prev = isPresent(prevNode) ? prevNode.value : null; + let outlet = this.getOutlet(parentOutletMap, currNode.value); - /** @internal */ - _startNavigating(): void { this.navigating = true; } - - /** @internal */ - _finishNavigating(): void { this.navigating = false; } - - - /** - * Subscribe to URL updates from the router - */ - subscribe(onNext: (value: any) => void, onError?: (value: any) => void): Object { - return ObservableWrapper.subscribe(this._subject, onNext, onError); - } - - - /** - * Removes the contents of this router's outlet and all descendant outlets - */ - deactivate(instruction: Instruction): Promise { - var childInstruction: Instruction = null; - var componentInstruction: ComponentInstruction = null; - if (isPresent(instruction)) { - childInstruction = instruction.child; - componentInstruction = instruction.component; + if (equalSegments(curr, prev)) { + this.loadChildSegments(currNode, prevNode, outlet.outletMap, + components.concat([outlet.loadedComponent])); + } else { + this.unloadOutlet(outlet, components); + if (this.performMutation) { + let outletMap = new RouterOutletMap(); + let loadedComponent = this.loadNewSegment(outletMap, curr, prev, outlet); + this.loadChildSegments(currNode, prevNode, outletMap, components.concat([loadedComponent])); + } } - var next: Promise = _resolveToTrue; - if (isPresent(this._childRouter)) { - next = this._childRouter.deactivate(childInstruction); + } + + private loadNewSegment(outletMap: RouterOutletMap, curr: RouteSegment, prev: RouteSegment, + outlet: RouterOutlet): Object { + let resolved = ReflectiveInjector.resolve( + [provide(RouterOutletMap, {useValue: outletMap}), provide(RouteSegment, {useValue: curr})]); + let ref = outlet.load(routeSegmentComponentFactory(curr), resolved, outletMap); + if (hasLifecycleHook("routerOnActivate", ref.instance)) { + ref.instance.routerOnActivate(curr, prev, this.currTree, this.prevTree); } - if (isPresent(this._outlet)) { - next = next.then((_) => this._outlet.deactivate(componentInstruction)); + return ref.instance; + } + + private getOutlet(outletMap: RouterOutletMap, segment: RouteSegment): RouterOutlet { + let outlet = outletMap._outlets[segment.outlet]; + if (isBlank(outlet)) { + if (segment.outlet == DEFAULT_OUTLET_NAME) { + throw new BaseException(`Cannot find default outlet`); + } else { + throw new BaseException(`Cannot find the outlet ${segment.outlet}`); + } } - - // TODO: handle aux routes - - return next; + return outlet; } - - /** - * Given a URL, returns an instruction representing the component graph - */ - recognize(url: string): Promise { - var ancestorComponents = this._getAncestorInstructions(); - return this.registry.recognize(url, ancestorComponents); - } - - private _getAncestorInstructions(): Instruction[] { - var ancestorInstructions: Instruction[] = [this.currentInstruction]; - var ancestorRouter: Router = this; - while (isPresent(ancestorRouter = ancestorRouter.parent)) { - ancestorInstructions.unshift(ancestorRouter.currentInstruction); - } - return ancestorInstructions; - } - - - /** - * Navigates to either the last URL successfully navigated to, or the last URL requested if the - * router has yet to successfully navigate. - */ - renavigate(): Promise { - if (isBlank(this.lastNavigationAttempt)) { - return this._currentNavigation; - } - return this.navigateByUrl(this.lastNavigationAttempt); - } - - - /** - * Generate an `Instruction` based on the provided Route Link DSL. - */ - generate(linkParams: any[]): Instruction { - var ancestorInstructions = this._getAncestorInstructions(); - return this.registry.generate(linkParams, ancestorInstructions); - } -} - -@Injectable() -export class RootRouter extends Router { - /** @internal */ - _location: Location; - /** @internal */ - _locationSub: Object; - - constructor(registry: RouteRegistry, location: Location, - @Inject(ROUTER_PRIMARY_COMPONENT) primaryComponent: Type) { - super(registry, null, primaryComponent); - this.root = this; - this._location = location; - this._locationSub = this._location.subscribe((change) => { - // we call recognize ourselves - this.recognize(change['url']) - .then((instruction) => { - if (isPresent(instruction)) { - this.navigateByInstruction(instruction, isPresent(change['pop'])) - .then((_) => { - // this is a popstate event; no need to change the URL - if (isPresent(change['pop']) && change['type'] != 'hashchange') { - return; - } - var emitPath = instruction.toUrlPath(); - var emitQuery = instruction.toUrlQuery(); - if (emitPath.length > 0 && emitPath[0] != '/') { - emitPath = '/' + emitPath; - } - - // We've opted to use pushstate and popState APIs regardless of whether you - // an app uses HashLocationStrategy or PathLocationStrategy. - // However, apps that are migrating might have hash links that operate outside - // angular to which routing must respond. - // Therefore we know that all hashchange events occur outside Angular. - // To support these cases where we respond to hashchanges and redirect as a - // result, we need to replace the top item on the stack. - if (change['type'] == 'hashchange') { - if (instruction.toRootUrl() != this._location.path()) { - this._location.replaceState(emitPath, emitQuery); - } - } else { - this._location.go(emitPath, emitQuery); - } - }); - } else { - this._emitNavigationFail(change['url']); - } - }); - }); - - this.registry.configFromComponent(primaryComponent); - this.navigateByUrl(location.path()); - } - - commit(instruction: Instruction, _skipLocationChange: boolean = false): Promise { - var emitPath = instruction.toUrlPath(); - var emitQuery = instruction.toUrlQuery(); - if (emitPath.length > 0 && emitPath[0] != '/') { - emitPath = '/' + emitPath; - } - var promise = super.commit(instruction); - if (!_skipLocationChange) { - promise = promise.then((_) => { this._location.go(emitPath, emitQuery); }); - } - return promise; - } - - dispose(): void { - if (isPresent(this._locationSub)) { - ObservableWrapper.dispose(this._locationSub); - this._locationSub = null; + private unloadOutlet(outlet: RouterOutlet, components: Object[]): void { + if (isPresent(outlet) && outlet.isLoaded) { + StringMapWrapper.forEach(outlet.outletMap._outlets, + (v, k) => this.unloadOutlet(v, components)); + if (this.performMutation) { + outlet.unload(); + } else { + this.deactivations.push(components.concat([outlet.loadedComponent])); + } } } } - -class ChildRouter extends Router { - constructor(parent: Router, hostComponent) { - super(parent.registry, parent, hostComponent, parent.root); - this.parent = parent; - } - - - navigateByUrl(url: string, _skipLocationChange: boolean = false): Promise { - // Delegate navigation to the root router - return this.parent.navigateByUrl(url, _skipLocationChange); - } - - navigateByInstruction(instruction: Instruction, - _skipLocationChange: boolean = false): Promise { - // Delegate navigation to the root router - return this.parent.navigateByInstruction(instruction, _skipLocationChange); - } -} - - -function canActivateOne(nextInstruction: Instruction, - prevInstruction: Instruction): Promise { - var next = _resolveToTrue; - if (isBlank(nextInstruction.component)) { - return next; - } - if (isPresent(nextInstruction.child)) { - next = canActivateOne(nextInstruction.child, - isPresent(prevInstruction) ? prevInstruction.child : null); - } - return next.then((result: boolean): boolean => { - if (result == false) { - return false; - } - if (nextInstruction.component.reuse) { - return true; - } - var hook = getCanActivateHook(nextInstruction.component.componentType); - if (isPresent(hook)) { - return hook(nextInstruction.component, - isPresent(prevInstruction) ? prevInstruction.component : null); - } - return true; - }); -} diff --git a/modules/@angular/router/src/router_providers.ts b/modules/@angular/router/src/router_providers.ts index baddb26ed0..1dfb6a3c7b 100644 --- a/modules/@angular/router/src/router_providers.ts +++ b/modules/@angular/router/src/router_providers.ts @@ -1,41 +1,8 @@ import {ROUTER_PROVIDERS_COMMON} from './router_providers_common'; -import {Provider} from '@angular/core'; import {BrowserPlatformLocation} from '@angular/platform-browser'; import {PlatformLocation} from '@angular/common'; -/** - * A list of {@link Provider}s. To use the router, you must add this to your application. - * - * ### Example ([live demo](http://plnkr.co/edit/iRUP8B5OUbxCWQ3AcIDm)) - * - * ``` - * import {Component} from '@angular/core'; - * import { - * ROUTER_DIRECTIVES, - * ROUTER_PROVIDERS, - * RouteConfig - * } from '@angular/router'; - * - * @Component({directives: [ROUTER_DIRECTIVES]}) - * @RouteConfig([ - * {...}, - * ]) - * class AppCmp { - * // ... - * } - * - * bootstrap(AppCmp, [ROUTER_PROVIDERS]); - * ``` - */ export const ROUTER_PROVIDERS: any[] = /*@ts2dart_const*/[ ROUTER_PROVIDERS_COMMON, - /*@ts2dart_const*/ ( - /* @ts2dart_Provider */ {provide: PlatformLocation, useClass: BrowserPlatformLocation}), + /*@ts2dart_Provider*/ {provide: PlatformLocation, useClass: BrowserPlatformLocation}, ]; - -/** - * Use {@link ROUTER_PROVIDERS} instead. - * - * @deprecated - */ -export const ROUTER_BINDINGS = /*@ts2dart_const*/ ROUTER_PROVIDERS; diff --git a/modules/@angular/router/src/router_providers_common.ts b/modules/@angular/router/src/router_providers_common.ts index a9042754e1..b3822ef38c 100644 --- a/modules/@angular/router/src/router_providers_common.ts +++ b/modules/@angular/router/src/router_providers_common.ts @@ -1,39 +1,31 @@ -import {ApplicationRef, Provider} from '@angular/core'; +import {OpaqueToken, ComponentResolver} from '@angular/core'; import {LocationStrategy, PathLocationStrategy, Location} from '@angular/common'; -import {Router, RootRouter} from './router'; -import {RouteRegistry, ROUTER_PRIMARY_COMPONENT} from './route_registry'; -import {Type} from '../src/facade/lang'; -import {BaseException} from '../src/facade/exceptions'; +import {Router, RouterOutletMap} from './router'; +import {RouterUrlSerializer, DefaultRouterUrlSerializer} from './router_url_serializer'; +import {ApplicationRef} from '@angular/core'; +import {BaseException} from '@angular/core'; -/** - * The Platform agnostic ROUTER PROVIDERS - */ export const ROUTER_PROVIDERS_COMMON: any[] = /*@ts2dart_const*/[ - RouteRegistry, - /* @ts2dart_Provider */ {provide: LocationStrategy, useClass: PathLocationStrategy}, - Location, - { + RouterOutletMap, + /*@ts2dart_Provider*/ {provide: RouterUrlSerializer, useClass: DefaultRouterUrlSerializer}, + /*@ts2dart_Provider*/ {provide: LocationStrategy, useClass: PathLocationStrategy}, Location, + /*@ts2dart_Provider*/ { provide: Router, useFactory: routerFactory, - deps: [RouteRegistry, Location, ROUTER_PRIMARY_COMPONENT, ApplicationRef] + deps: /*@ts2dart_const*/ + [ApplicationRef, ComponentResolver, RouterUrlSerializer, RouterOutletMap, Location], }, - { - provide: ROUTER_PRIMARY_COMPONENT, - useFactory: routerPrimaryComponentFactory, - deps: /*@ts2dart_const*/ ([ApplicationRef]) - } ]; -function routerFactory(registry: RouteRegistry, location: Location, primaryComponent: Type, - appRef: ApplicationRef): RootRouter { - var rootRouter = new RootRouter(registry, location, primaryComponent); - appRef.registerDisposeListener(() => rootRouter.dispose()); - return rootRouter; -} - -function routerPrimaryComponentFactory(app: ApplicationRef): Type { +function routerFactory(app: ApplicationRef, componentResolver: ComponentResolver, + urlSerializer: RouterUrlSerializer, routerOutletMap: RouterOutletMap, + location: Location): Router { if (app.componentTypes.length == 0) { throw new BaseException("Bootstrap at least one component before injecting Router."); } - return app.componentTypes[0]; + // TODO: vsavkin this should not be null + let router = new Router(null, app.componentTypes[0], componentResolver, urlSerializer, + routerOutletMap, location); + app.registerDisposeListener(() => router.dispose()); + return router; } diff --git a/modules/angular2/src/alt_router/router_url_serializer.ts b/modules/@angular/router/src/router_url_serializer.ts similarity index 100% rename from modules/angular2/src/alt_router/router_url_serializer.ts rename to modules/@angular/router/src/router_url_serializer.ts diff --git a/modules/angular2/src/alt_router/segments.ts b/modules/@angular/router/src/segments.ts similarity index 100% rename from modules/angular2/src/alt_router/segments.ts rename to modules/@angular/router/src/segments.ts diff --git a/modules/angular2/test/alt_router/integration_spec.ts b/modules/@angular/router/test/integration_spec.ts similarity index 100% rename from modules/angular2/test/alt_router/integration_spec.ts rename to modules/@angular/router/test/integration_spec.ts diff --git a/modules/angular2/test/alt_router/link_spec.ts b/modules/@angular/router/test/link_spec.ts similarity index 100% rename from modules/angular2/test/alt_router/link_spec.ts rename to modules/@angular/router/test/link_spec.ts diff --git a/modules/angular2/test/alt_router/recognize_spec.ts b/modules/@angular/router/test/recognize_spec.ts similarity index 100% rename from modules/angular2/test/alt_router/recognize_spec.ts rename to modules/@angular/router/test/recognize_spec.ts diff --git a/modules/angular2/test/alt_router/router_url_serializer_spec.ts b/modules/@angular/router/test/router_url_serializer_spec.ts similarity index 100% rename from modules/angular2/test/alt_router/router_url_serializer_spec.ts rename to modules/@angular/router/test/router_url_serializer_spec.ts diff --git a/modules/angular2/test/alt_router/tree_spec.ts b/modules/@angular/router/test/tree_spec.ts similarity index 100% rename from modules/angular2/test/alt_router/tree_spec.ts rename to modules/@angular/router/test/tree_spec.ts diff --git a/modules/angular2/src/alt_router/router_testing_providers.ts b/modules/@angular/router/testing/router_testing_providers.ts similarity index 100% rename from modules/angular2/src/alt_router/router_testing_providers.ts rename to modules/@angular/router/testing/router_testing_providers.ts diff --git a/modules/angular2/alt_router.ts b/modules/angular2/alt_router.ts deleted file mode 100644 index d7ea77a688..0000000000 --- a/modules/angular2/alt_router.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * @module - * @description - * Alternative implementation of the router. Experimental. - */ - -export {Router, RouterOutletMap} from './src/alt_router/router'; -export {RouteSegment, UrlSegment, Tree, UrlTree, RouteTree} from './src/alt_router/segments'; -export {Routes} from './src/alt_router/metadata/decorators'; -export {Route} from './src/alt_router/metadata/metadata'; -export { - RouterUrlSerializer, - DefaultRouterUrlSerializer -} from './src/alt_router/router_url_serializer'; -export {OnActivate, CanDeactivate} from './src/alt_router/interfaces'; -export {ROUTER_PROVIDERS} from './src/alt_router/router_providers'; - -import {RouterOutlet} from './src/alt_router/directives/router_outlet'; -import {RouterLink} from './src/alt_router/directives/router_link'; - -export const ROUTER_DIRECTIVES: any[] = /*@ts2dart_const*/[RouterOutlet, RouterLink]; diff --git a/modules/angular2/src/alt_router/directives/router_link.ts b/modules/angular2/src/alt_router/directives/router_link.ts deleted file mode 100644 index 8c662d3e38..0000000000 --- a/modules/angular2/src/alt_router/directives/router_link.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { - ResolvedReflectiveProvider, - Directive, - DynamicComponentLoader, - ViewContainerRef, - Attribute, - ComponentRef, - ComponentFactory, - ReflectiveInjector, - OnInit, - HostListener, - HostBinding, - Input, - OnDestroy, - Optional -} from '@angular/core'; -import {RouterOutletMap, Router} from '../router'; -import {RouteSegment, UrlSegment, Tree} from '../segments'; -import {isString, isPresent} from '@angular/facade/src/lang'; -import {ObservableWrapper} from '@angular/facade/src/async'; - -@Directive({selector: '[routerLink]'}) -export class RouterLink implements OnDestroy { - @Input() target: string; - private _changes: any[] = []; - private _subscription: any; - - @HostBinding() private href: string; - @HostBinding('class.router-link-active') private isActive: boolean = false; - - constructor(@Optional() private _routeSegment: RouteSegment, private _router: Router) { - this._subscription = - ObservableWrapper.subscribe(_router.changes, (_) => { this._updateTargetUrlAndHref(); }); - } - - ngOnDestroy() { ObservableWrapper.dispose(this._subscription); } - - @Input() - set routerLink(data: any[]) { - this._changes = data; - this._updateTargetUrlAndHref(); - } - - @HostListener("click") - onClick(): boolean { - if (!isString(this.target) || this.target == '_self') { - this._router.navigate(this._changes, this._routeSegment); - return false; - } - return true; - } - - private _updateTargetUrlAndHref(): void { - let tree = this._router.createUrlTree(this._changes, this._routeSegment); - if (isPresent(tree)) { - this.href = this._router.serializeUrl(tree); - this.isActive = this._router.urlTree.contains(tree); - } else { - this.isActive = false; - } - } -} diff --git a/modules/angular2/src/alt_router/directives/router_outlet.ts b/modules/angular2/src/alt_router/directives/router_outlet.ts deleted file mode 100644 index fbdb041d95..0000000000 --- a/modules/angular2/src/alt_router/directives/router_outlet.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { - ResolvedReflectiveProvider, - Directive, - DynamicComponentLoader, - ViewContainerRef, - Attribute, - ComponentRef, - ComponentFactory, - ReflectiveInjector, - OnInit -} from '@angular/core'; -import {RouterOutletMap} from '../router'; -import {DEFAULT_OUTLET_NAME} from '../constants'; -import {isPresent, isBlank} from '@angular/facade/src/lang'; - -@Directive({selector: 'router-outlet'}) -export class RouterOutlet { - private _loaded: ComponentRef; - public outletMap: RouterOutletMap; - - constructor(parentOutletMap: RouterOutletMap, private _location: ViewContainerRef, - @Attribute('name') name: string) { - parentOutletMap.registerOutlet(isBlank(name) ? DEFAULT_OUTLET_NAME : name, this); - } - - unload(): void { - this._loaded.destroy(); - this._loaded = null; - } - - get loadedComponent(): Object { return isPresent(this._loaded) ? this._loaded.instance : null; } - - get isLoaded(): boolean { return isPresent(this._loaded); } - - load(factory: ComponentFactory, providers: ResolvedReflectiveProvider[], - outletMap: RouterOutletMap): ComponentRef { - this.outletMap = outletMap; - let inj = ReflectiveInjector.fromResolvedProviders(providers, this._location.parentInjector); - this._loaded = this._location.createComponent(factory, this._location.length, inj, []); - return this._loaded; - } -} diff --git a/modules/angular2/src/alt_router/interfaces.ts b/modules/angular2/src/alt_router/interfaces.ts deleted file mode 100644 index c09b38df5f..0000000000 --- a/modules/angular2/src/alt_router/interfaces.ts +++ /dev/null @@ -1,10 +0,0 @@ -import {RouteSegment, Tree, RouteTree} from './segments'; - -export interface OnActivate { - routerOnActivate(curr: RouteSegment, prev?: RouteSegment, currTree?: RouteTree, - prevTree?: RouteTree): void; -} - -export interface CanDeactivate { - routerCanDeactivate(currTree?: RouteTree, futureTree?: RouteTree): Promise; -} \ No newline at end of file diff --git a/modules/angular2/src/alt_router/router.ts b/modules/angular2/src/alt_router/router.ts deleted file mode 100644 index 2c5bc6751e..0000000000 --- a/modules/angular2/src/alt_router/router.ts +++ /dev/null @@ -1,226 +0,0 @@ -import {OnInit, provide, ReflectiveInjector, ComponentResolver} from '@angular/core'; -import {RouterOutlet} from './directives/router_outlet'; -import {Type, isBlank, isPresent} from '@angular/facade/src/lang'; -import {ListWrapper} from '@angular/facade/src/collection'; -import { - EventEmitter, - Observable, - PromiseWrapper, - ObservableWrapper -} from '@angular/facade/src/async'; -import {StringMapWrapper} from '@angular/facade/src/collection'; -import {BaseException} from '@angular/core'; -import {RouterUrlSerializer} from './router_url_serializer'; -import {CanDeactivate} from './interfaces'; -import {recognize} from './recognize'; -import {Location} from '@angular/common'; -import {link} from './link'; - -import { - equalSegments, - routeSegmentComponentFactory, - RouteSegment, - UrlTree, - RouteTree, - rootNode, - TreeNode, - UrlSegment, - serializeRouteSegmentTree -} from './segments'; -import {hasLifecycleHook} from './lifecycle_reflector'; -import {DEFAULT_OUTLET_NAME} from './constants'; - -export class RouterOutletMap { - /** @internal */ - _outlets: {[name: string]: RouterOutlet} = {}; - registerOutlet(name: string, outlet: RouterOutlet): void { this._outlets[name] = outlet; } -} - -export class Router { - private _prevTree: RouteTree; - private _urlTree: UrlTree; - private _locationSubscription: any; - private _changes: EventEmitter = new EventEmitter(); - - constructor(private _rootComponent: Object, private _rootComponentType: Type, - private _componentResolver: ComponentResolver, - private _urlSerializer: RouterUrlSerializer, - private _routerOutletMap: RouterOutletMap, private _location: Location) { - this._prevTree = this._createInitialTree(); - this._setUpLocationChangeListener(); - this.navigateByUrl(this._location.path()); - } - - get urlTree(): UrlTree { return this._urlTree; } - - navigateByUrl(url: string): Promise { - return this._navigate(this._urlSerializer.parse(url)); - } - - navigate(changes: any[], segment?: RouteSegment): Promise { - return this._navigate(this.createUrlTree(changes, segment)); - } - - dispose(): void { ObservableWrapper.dispose(this._locationSubscription); } - - private _createInitialTree(): RouteTree { - let root = new RouteSegment([new UrlSegment("", null, null)], null, DEFAULT_OUTLET_NAME, - this._rootComponentType, null); - return new RouteTree(new TreeNode(root, [])); - } - - private _setUpLocationChangeListener(): void { - this._locationSubscription = this._location.subscribe( - (change) => { this._navigate(this._urlSerializer.parse(change['url'])); }); - } - - private _navigate(url: UrlTree): Promise { - this._urlTree = url; - return recognize(this._componentResolver, this._rootComponentType, url) - .then(currTree => { - return new _LoadSegments(currTree, this._prevTree) - .load(this._routerOutletMap, this._rootComponent) - .then(updated => { - if (updated) { - this._prevTree = currTree; - this._location.go(this._urlSerializer.serialize(this._urlTree)); - this._changes.emit(null); - } - }); - }); - } - - createUrlTree(changes: any[], segment?: RouteSegment): UrlTree { - if (isPresent(this._prevTree)) { - let s = isPresent(segment) ? segment : this._prevTree.root; - return link(s, this._prevTree, this.urlTree, changes); - } else { - return null; - } - } - - serializeUrl(url: UrlTree): string { return this._urlSerializer.serialize(url); } - - get changes(): Observable { return this._changes; } - - get routeTree(): RouteTree { return this._prevTree; } -} - - -class _LoadSegments { - private deactivations: Object[][] = []; - private performMutation: boolean = true; - - constructor(private currTree: RouteTree, private prevTree: RouteTree) {} - - load(parentOutletMap: RouterOutletMap, rootComponent: Object): Promise { - let prevRoot = isPresent(this.prevTree) ? rootNode(this.prevTree) : null; - let currRoot = rootNode(this.currTree); - - return this.canDeactivate(currRoot, prevRoot, parentOutletMap, rootComponent) - .then(res => { - this.performMutation = true; - if (res) { - this.loadChildSegments(currRoot, prevRoot, parentOutletMap, [rootComponent]); - } - return res; - }); - } - - private canDeactivate(currRoot: TreeNode, prevRoot: TreeNode, - outletMap: RouterOutletMap, rootComponent: Object): Promise { - this.performMutation = false; - this.loadChildSegments(currRoot, prevRoot, outletMap, [rootComponent]); - - let allPaths = PromiseWrapper.all(this.deactivations.map(r => this.checkCanDeactivatePath(r))); - return allPaths.then((values: boolean[]) => values.filter(v => v).length === values.length); - } - - private checkCanDeactivatePath(path: Object[]): Promise { - let curr = PromiseWrapper.resolve(true); - for (let p of ListWrapper.reversed(path)) { - curr = curr.then(_ => { - if (hasLifecycleHook("routerCanDeactivate", p)) { - return (p).routerCanDeactivate(this.prevTree, this.currTree); - } else { - return _; - } - }); - } - return curr; - } - - private loadChildSegments(currNode: TreeNode, prevNode: TreeNode, - outletMap: RouterOutletMap, components: Object[]): void { - let prevChildren = isPresent(prevNode) ? - prevNode.children.reduce( - (m, c) => { - m[c.value.outlet] = c; - return m; - }, - {}) : - {}; - - currNode.children.forEach(c => { - this.loadSegments(c, prevChildren[c.value.outlet], outletMap, components); - StringMapWrapper.delete(prevChildren, c.value.outlet); - }); - - StringMapWrapper.forEach(prevChildren, - (v, k) => this.unloadOutlet(outletMap._outlets[k], components)); - } - - loadSegments(currNode: TreeNode, prevNode: TreeNode, - parentOutletMap: RouterOutletMap, components: Object[]): void { - let curr = currNode.value; - let prev = isPresent(prevNode) ? prevNode.value : null; - let outlet = this.getOutlet(parentOutletMap, currNode.value); - - if (equalSegments(curr, prev)) { - this.loadChildSegments(currNode, prevNode, outlet.outletMap, - components.concat([outlet.loadedComponent])); - } else { - this.unloadOutlet(outlet, components); - if (this.performMutation) { - let outletMap = new RouterOutletMap(); - let loadedComponent = this.loadNewSegment(outletMap, curr, prev, outlet); - this.loadChildSegments(currNode, prevNode, outletMap, components.concat([loadedComponent])); - } - } - } - - private loadNewSegment(outletMap: RouterOutletMap, curr: RouteSegment, prev: RouteSegment, - outlet: RouterOutlet): Object { - let resolved = ReflectiveInjector.resolve( - [provide(RouterOutletMap, {useValue: outletMap}), provide(RouteSegment, {useValue: curr})]); - let ref = outlet.load(routeSegmentComponentFactory(curr), resolved, outletMap); - if (hasLifecycleHook("routerOnActivate", ref.instance)) { - ref.instance.routerOnActivate(curr, prev, this.currTree, this.prevTree); - } - return ref.instance; - } - - private getOutlet(outletMap: RouterOutletMap, segment: RouteSegment): RouterOutlet { - let outlet = outletMap._outlets[segment.outlet]; - if (isBlank(outlet)) { - if (segment.outlet == DEFAULT_OUTLET_NAME) { - throw new BaseException(`Cannot find default outlet`); - } else { - throw new BaseException(`Cannot find the outlet ${segment.outlet}`); - } - } - return outlet; - } - - private unloadOutlet(outlet: RouterOutlet, components: Object[]): void { - if (isPresent(outlet) && outlet.isLoaded) { - StringMapWrapper.forEach(outlet.outletMap._outlets, - (v, k) => this.unloadOutlet(v, components)); - if (this.performMutation) { - outlet.unload(); - } else { - this.deactivations.push(components.concat([outlet.loadedComponent])); - } - } - } -} diff --git a/modules/angular2/src/alt_router/router_providers.ts b/modules/angular2/src/alt_router/router_providers.ts deleted file mode 100644 index 1dfb6a3c7b..0000000000 --- a/modules/angular2/src/alt_router/router_providers.ts +++ /dev/null @@ -1,8 +0,0 @@ -import {ROUTER_PROVIDERS_COMMON} from './router_providers_common'; -import {BrowserPlatformLocation} from '@angular/platform-browser'; -import {PlatformLocation} from '@angular/common'; - -export const ROUTER_PROVIDERS: any[] = /*@ts2dart_const*/[ - ROUTER_PROVIDERS_COMMON, - /*@ts2dart_Provider*/ {provide: PlatformLocation, useClass: BrowserPlatformLocation}, -]; diff --git a/modules/angular2/src/alt_router/router_providers_common.ts b/modules/angular2/src/alt_router/router_providers_common.ts deleted file mode 100644 index b3822ef38c..0000000000 --- a/modules/angular2/src/alt_router/router_providers_common.ts +++ /dev/null @@ -1,31 +0,0 @@ -import {OpaqueToken, ComponentResolver} from '@angular/core'; -import {LocationStrategy, PathLocationStrategy, Location} from '@angular/common'; -import {Router, RouterOutletMap} from './router'; -import {RouterUrlSerializer, DefaultRouterUrlSerializer} from './router_url_serializer'; -import {ApplicationRef} from '@angular/core'; -import {BaseException} from '@angular/core'; - -export const ROUTER_PROVIDERS_COMMON: any[] = /*@ts2dart_const*/[ - RouterOutletMap, - /*@ts2dart_Provider*/ {provide: RouterUrlSerializer, useClass: DefaultRouterUrlSerializer}, - /*@ts2dart_Provider*/ {provide: LocationStrategy, useClass: PathLocationStrategy}, Location, - /*@ts2dart_Provider*/ { - provide: Router, - useFactory: routerFactory, - deps: /*@ts2dart_const*/ - [ApplicationRef, ComponentResolver, RouterUrlSerializer, RouterOutletMap, Location], - }, -]; - -function routerFactory(app: ApplicationRef, componentResolver: ComponentResolver, - urlSerializer: RouterUrlSerializer, routerOutletMap: RouterOutletMap, - location: Location): Router { - if (app.componentTypes.length == 0) { - throw new BaseException("Bootstrap at least one component before injecting Router."); - } - // TODO: vsavkin this should not be null - let router = new Router(null, app.componentTypes[0], componentResolver, urlSerializer, - routerOutletMap, location); - app.registerDisposeListener(() => router.dispose()); - return router; -} diff --git a/modules/angular2/src/core/di/provider_util.dart b/modules/angular2/src/core/di/provider_util.dart deleted file mode 100644 index 9b83032815..0000000000 --- a/modules/angular2/src/core/di/provider_util.dart +++ /dev/null @@ -1,20 +0,0 @@ -import './provider.dart' show Provider; - -bool isProviderLiteral(dynamic obj) { - if (obj is Map) { - Map map = obj as Map; - return map.containsKey('provide'); - } else { - return false; - } -} - -Provider createProvider(dynamic obj) { - Map map = obj as Map; - return new Provider(map['provide'], useClass: map['useClass'], - useValue: map['useValue'], - useExisting: map['useExisting'], - useFactory: map['useFactory'], - deps: map['deps'], - multi: map['multi']); -} diff --git a/modules/angular2/src/core/di/provider_util.ts b/modules/angular2/src/core/di/provider_util.ts deleted file mode 100644 index 0b22cdb6fb..0000000000 --- a/modules/angular2/src/core/di/provider_util.ts +++ /dev/null @@ -1,9 +0,0 @@ -import {Provider} from './provider'; - -export function isProviderLiteral(obj: any): boolean { - return obj && typeof obj == 'object' && obj.provide; -} - -export function createProvider(obj: any): Provider { - return new Provider(obj.provide, obj); -}