2015-08-20 14:28:25 -07:00
|
|
|
import {Promise, PromiseWrapper} from 'angular2/src/core/facade/async';
|
|
|
|
import {StringMapWrapper} from 'angular2/src/core/facade/collection';
|
2015-10-08 09:57:10 -07:00
|
|
|
import {isBlank, isPresent} from 'angular2/src/core/facade/lang';
|
2015-09-10 15:25:36 -07:00
|
|
|
import {BaseException, WrappedException} from 'angular2/src/core/facade/exceptions';
|
2015-04-17 09:59:56 -07:00
|
|
|
|
2015-10-11 13:28:56 +02:00
|
|
|
import {
|
|
|
|
Directive,
|
|
|
|
Attribute,
|
|
|
|
DynamicComponentLoader,
|
|
|
|
ComponentRef,
|
|
|
|
ElementRef,
|
|
|
|
Injector,
|
|
|
|
provide,
|
|
|
|
Dependency
|
|
|
|
} from 'angular2/angular2';
|
2015-04-17 09:59:56 -07:00
|
|
|
|
|
|
|
import * as routerMod from './router';
|
2015-09-30 14:48:58 +02:00
|
|
|
import {ComponentInstruction, RouteParams, RouteData} from './instruction';
|
2015-07-07 15:44:29 -07:00
|
|
|
import * as hookMod from './lifecycle_annotations';
|
|
|
|
import {hasLifecycleHook} from './route_lifecycle_reflector';
|
2015-05-14 13:01:48 -07:00
|
|
|
|
2015-08-30 21:25:46 -07:00
|
|
|
let _resolveToTrue = PromiseWrapper.resolve(true);
|
|
|
|
|
2015-05-14 13:01:48 -07:00
|
|
|
/**
|
|
|
|
* A router outlet is a placeholder that Angular dynamically fills based on the application's route.
|
|
|
|
*
|
|
|
|
* ## Use
|
|
|
|
*
|
|
|
|
* ```
|
|
|
|
* <router-outlet></router-outlet>
|
|
|
|
* ```
|
|
|
|
*/
|
2015-10-06 06:53:39 -07:00
|
|
|
@Directive({selector: 'router-outlet'})
|
2015-10-08 08:22:11 -07:00
|
|
|
export class RouterOutlet {
|
|
|
|
name: string = null;
|
2015-10-06 06:53:39 -07:00
|
|
|
private _componentRef: ComponentRef = null;
|
|
|
|
private _currentInstruction: ComponentInstruction = null;
|
|
|
|
|
2015-06-29 11:15:49 -07:00
|
|
|
constructor(private _elementRef: ElementRef, private _loader: DynamicComponentLoader,
|
|
|
|
private _parentRouter: routerMod.Router, @Attribute('name') nameAttr: string) {
|
2015-07-17 13:36:53 -07:00
|
|
|
if (isPresent(nameAttr)) {
|
|
|
|
this.name = nameAttr;
|
2015-08-30 21:25:46 -07:00
|
|
|
this._parentRouter.registerAuxOutlet(this);
|
2015-07-17 13:36:53 -07:00
|
|
|
} else {
|
2015-08-30 21:25:46 -07:00
|
|
|
this._parentRouter.registerPrimaryOutlet(this);
|
2015-07-17 13:36:53 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-08 08:22:11 -07:00
|
|
|
/**
|
|
|
|
* 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 `onActivate` hook of its child.
|
|
|
|
*/
|
2015-08-30 21:25:46 -07:00
|
|
|
activate(nextInstruction: ComponentInstruction): Promise<any> {
|
2015-07-07 15:44:29 -07:00
|
|
|
var previousInstruction = this._currentInstruction;
|
2015-08-30 21:25:46 -07:00
|
|
|
this._currentInstruction = nextInstruction;
|
|
|
|
var componentType = nextInstruction.componentType;
|
|
|
|
var childRouter = this._parentRouter.childRouter(componentType);
|
2015-07-07 15:44:29 -07:00
|
|
|
|
2015-10-10 22:11:13 -07:00
|
|
|
var providers = Injector.resolve([
|
2015-09-30 14:48:58 +02:00
|
|
|
provide(RouteData, {useValue: nextInstruction.routeData}),
|
2015-10-12 11:30:34 -07:00
|
|
|
provide(RouteParams, {useValue: new RouteParams(nextInstruction.params)}),
|
|
|
|
provide(routerMod.Router, {useValue: childRouter})
|
2015-07-07 15:44:29 -07:00
|
|
|
]);
|
2015-10-10 22:11:13 -07:00
|
|
|
return this._loader.loadNextToLocation(componentType, this._elementRef, providers)
|
2015-05-29 14:58:41 -07:00
|
|
|
.then((componentRef) => {
|
|
|
|
this._componentRef = componentRef;
|
2015-07-17 13:36:53 -07:00
|
|
|
if (hasLifecycleHook(hookMod.onActivate, componentType)) {
|
2015-08-30 21:25:46 -07:00
|
|
|
return this._componentRef.instance.onActivate(nextInstruction, previousInstruction);
|
2015-07-07 15:44:29 -07:00
|
|
|
}
|
2015-05-29 14:58:41 -07:00
|
|
|
});
|
2015-04-17 09:59:56 -07:00
|
|
|
}
|
|
|
|
|
2015-10-08 08:22:11 -07:00
|
|
|
/**
|
|
|
|
* 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 `onReuse` hook of its child.
|
|
|
|
*/
|
2015-08-30 21:25:46 -07:00
|
|
|
reuse(nextInstruction: ComponentInstruction): Promise<any> {
|
|
|
|
var previousInstruction = this._currentInstruction;
|
|
|
|
this._currentInstruction = nextInstruction;
|
|
|
|
|
|
|
|
if (isBlank(this._componentRef)) {
|
|
|
|
throw new BaseException(`Cannot reuse an outlet that does not contain a component.`);
|
|
|
|
}
|
|
|
|
return PromiseWrapper.resolve(
|
|
|
|
hasLifecycleHook(hookMod.onReuse, this._currentInstruction.componentType) ?
|
|
|
|
this._componentRef.instance.onReuse(nextInstruction, previousInstruction) :
|
|
|
|
true);
|
|
|
|
}
|
|
|
|
|
2015-10-08 08:22:11 -07:00
|
|
|
/**
|
2015-10-14 21:23:59 -05:00
|
|
|
* Called by the {@link Router} when an outlet disposes of a component's contents.
|
|
|
|
* This method in turn is responsible for calling the `onDeactivate` hook of its child.
|
2015-10-08 08:22:11 -07:00
|
|
|
*/
|
2015-08-30 21:25:46 -07:00
|
|
|
deactivate(nextInstruction: ComponentInstruction): Promise<any> {
|
|
|
|
var next = _resolveToTrue;
|
|
|
|
if (isPresent(this._componentRef) && isPresent(this._currentInstruction) &&
|
|
|
|
hasLifecycleHook(hookMod.onDeactivate, this._currentInstruction.componentType)) {
|
|
|
|
next = PromiseWrapper.resolve(
|
|
|
|
this._componentRef.instance.onDeactivate(nextInstruction, this._currentInstruction));
|
|
|
|
}
|
|
|
|
return next.then((_) => {
|
|
|
|
if (isPresent(this._componentRef)) {
|
|
|
|
this._componentRef.dispose();
|
|
|
|
this._componentRef = null;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2015-05-21 13:59:14 -07:00
|
|
|
|
2015-10-08 08:22:11 -07:00
|
|
|
/**
|
|
|
|
* 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 `canDeactivate` hook if it exists,
|
|
|
|
* and otherwise resolves to true.
|
|
|
|
*/
|
2015-08-30 21:25:46 -07:00
|
|
|
canDeactivate(nextInstruction: ComponentInstruction): Promise<boolean> {
|
2015-07-07 15:44:29 -07:00
|
|
|
if (isBlank(this._currentInstruction)) {
|
2015-08-30 21:25:46 -07:00
|
|
|
return _resolveToTrue;
|
2015-07-07 15:44:29 -07:00
|
|
|
}
|
2015-07-17 13:36:53 -07:00
|
|
|
if (hasLifecycleHook(hookMod.canDeactivate, this._currentInstruction.componentType)) {
|
2015-08-30 21:25:46 -07:00
|
|
|
return PromiseWrapper.resolve(
|
|
|
|
this._componentRef.instance.canDeactivate(nextInstruction, this._currentInstruction));
|
2015-07-07 15:44:29 -07:00
|
|
|
}
|
2015-08-30 21:25:46 -07:00
|
|
|
return _resolveToTrue;
|
2015-07-07 15:44:29 -07:00
|
|
|
}
|
|
|
|
|
2015-10-08 08:22:11 -07:00
|
|
|
/**
|
|
|
|
* 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 `canReuse` hook if it exists,
|
|
|
|
* or resolves to true if the hook is not present.
|
|
|
|
*/
|
2015-08-30 21:25:46 -07:00
|
|
|
canReuse(nextInstruction: ComponentInstruction): Promise<boolean> {
|
2015-07-07 15:44:29 -07:00
|
|
|
var result;
|
2015-07-17 13:36:53 -07:00
|
|
|
|
2015-07-07 15:44:29 -07:00
|
|
|
if (isBlank(this._currentInstruction) ||
|
2015-08-30 21:25:46 -07:00
|
|
|
this._currentInstruction.componentType != nextInstruction.componentType) {
|
2015-07-07 15:44:29 -07:00
|
|
|
result = false;
|
2015-07-17 13:36:53 -07:00
|
|
|
} else if (hasLifecycleHook(hookMod.canReuse, this._currentInstruction.componentType)) {
|
2015-08-30 21:25:46 -07:00
|
|
|
result = this._componentRef.instance.canReuse(nextInstruction, this._currentInstruction);
|
2015-07-07 15:44:29 -07:00
|
|
|
} else {
|
2015-08-30 21:25:46 -07:00
|
|
|
result = nextInstruction == this._currentInstruction ||
|
|
|
|
(isPresent(nextInstruction.params) && isPresent(this._currentInstruction.params) &&
|
|
|
|
StringMapWrapper.equals(nextInstruction.params, this._currentInstruction.params));
|
2015-07-07 15:44:29 -07:00
|
|
|
}
|
2015-08-30 20:37:23 -07:00
|
|
|
return PromiseWrapper.resolve(result);
|
2015-07-07 15:44:29 -07:00
|
|
|
}
|
2015-04-17 09:59:56 -07:00
|
|
|
}
|