feat(router): implement Router.isRouteActive
This commit is contained in:
parent
e1a7e0329c
commit
de37729823
|
@ -132,62 +132,41 @@ function ngOutletDirective($animate, $injector, $q, $router, $componentMapper, $
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
router.registerOutlet({
|
router.registerPrimaryOutlet({
|
||||||
commit: function (instruction) {
|
reuse: function (instruction) {
|
||||||
var next = $q.when(true);
|
var next = $q.when(true);
|
||||||
var componentInstruction = instruction.component;
|
|
||||||
if (componentInstruction.reuse) {
|
|
||||||
var previousInstruction = currentInstruction;
|
var previousInstruction = currentInstruction;
|
||||||
currentInstruction = componentInstruction;
|
currentInstruction = instruction;
|
||||||
if (currentController.onReuse) {
|
if (currentController.onReuse) {
|
||||||
next = $q.when(currentController.onReuse(currentInstruction, previousInstruction));
|
next = $q.when(currentController.onReuse(currentInstruction, previousInstruction));
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
var self = this;
|
return next;
|
||||||
next = this.deactivate(instruction).then(function () {
|
|
||||||
return self.activate(componentInstruction);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return next.then(function () {
|
|
||||||
if (childRouter) {
|
|
||||||
return childRouter.commit(instruction.child);
|
|
||||||
} else {
|
|
||||||
return $q.when(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
canReuse: function (nextInstruction) {
|
canReuse: function (nextInstruction) {
|
||||||
var result;
|
var result;
|
||||||
var componentInstruction = nextInstruction.component;
|
|
||||||
if (!currentInstruction ||
|
if (!currentInstruction ||
|
||||||
currentInstruction.componentType !== componentInstruction.componentType) {
|
currentInstruction.componentType !== nextInstruction.componentType) {
|
||||||
result = false;
|
result = false;
|
||||||
} else if (currentController.canReuse) {
|
} else if (currentController.canReuse) {
|
||||||
result = currentController.canReuse(componentInstruction, currentInstruction);
|
result = currentController.canReuse(nextInstruction, currentInstruction);
|
||||||
} else {
|
} else {
|
||||||
result = componentInstruction === currentInstruction ||
|
result = nextInstruction === currentInstruction ||
|
||||||
angular.equals(componentInstruction.params, currentInstruction.params);
|
angular.equals(nextInstruction.params, currentInstruction.params);
|
||||||
}
|
}
|
||||||
return $q.when(result).then(function (result) {
|
return $q.when(result);
|
||||||
// TODO: this is a hack
|
|
||||||
componentInstruction.reuse = result;
|
|
||||||
return result;
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
canDeactivate: function (instruction) {
|
canDeactivate: function (instruction) {
|
||||||
if (currentInstruction && currentController && currentController.canDeactivate) {
|
if (currentInstruction && currentController && currentController.canDeactivate) {
|
||||||
return $q.when(currentController.canDeactivate(instruction && instruction.component, currentInstruction));
|
return $q.when(currentController.canDeactivate(instruction, currentInstruction));
|
||||||
}
|
}
|
||||||
return $q.when(true);
|
return $q.when(true);
|
||||||
},
|
},
|
||||||
deactivate: function (instruction) {
|
deactivate: function (instruction) {
|
||||||
// todo(shahata): childRouter.dectivate, dispose component?
|
|
||||||
var result = $q.when();
|
|
||||||
return result.then(function () {
|
|
||||||
if (currentController && currentController.onDeactivate) {
|
if (currentController && currentController.onDeactivate) {
|
||||||
return currentController.onDeactivate(instruction && instruction.component, currentInstruction);
|
return $q.when(currentController.onDeactivate(instruction, currentInstruction));
|
||||||
}
|
}
|
||||||
});
|
return $q.when();
|
||||||
},
|
},
|
||||||
activate: function (instruction) {
|
activate: function (instruction) {
|
||||||
var previousInstruction = currentInstruction;
|
var previousInstruction = currentInstruction;
|
||||||
|
@ -228,7 +207,7 @@ function ngOutletDirective($animate, $injector, $q, $router, $componentMapper, $
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, outletName);
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -54,11 +54,16 @@ export class Router {
|
||||||
lastNavigationAttempt: string;
|
lastNavigationAttempt: string;
|
||||||
|
|
||||||
private _currentInstruction: Instruction = null;
|
private _currentInstruction: Instruction = null;
|
||||||
|
|
||||||
private _currentNavigation: Promise<any> = _resolveToTrue;
|
private _currentNavigation: Promise<any> = _resolveToTrue;
|
||||||
private _outlet: RouterOutlet = null;
|
private _outlet: RouterOutlet = null;
|
||||||
private _auxOutlets: Map<string, RouterOutlet> = new Map();
|
|
||||||
|
private _auxRouters: Map<string, Router> = new Map();
|
||||||
|
private _childRouter: Router;
|
||||||
|
|
||||||
private _subject: EventEmitter = new EventEmitter();
|
private _subject: EventEmitter = new EventEmitter();
|
||||||
|
|
||||||
|
|
||||||
constructor(public registry: RouteRegistry, public _pipeline: Pipeline, public parent: Router,
|
constructor(public registry: RouteRegistry, public _pipeline: Pipeline, public parent: Router,
|
||||||
public hostComponent: any) {}
|
public hostComponent: any) {}
|
||||||
|
|
||||||
|
@ -67,25 +72,74 @@ export class Router {
|
||||||
* Constructs a child router. You probably don't need to use this unless you're writing a reusable
|
* Constructs a child router. You probably don't need to use this unless you're writing a reusable
|
||||||
* component.
|
* component.
|
||||||
*/
|
*/
|
||||||
childRouter(hostComponent: any): Router { return new ChildRouter(this, hostComponent); }
|
childRouter(hostComponent: any): Router {
|
||||||
|
return this._childRouter = new ChildRouter(this, hostComponent);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register an object to notify of route changes. You probably don't need to use this unless
|
* Constructs a child router. You probably don't need to use this unless you're writing a reusable
|
||||||
* you're writing a reusable component.
|
* component.
|
||||||
*/
|
*/
|
||||||
registerOutlet(outlet: RouterOutlet): Promise<boolean> {
|
auxRouter(hostComponent: any): Router { return new ChildRouter(this, hostComponent); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register an outlet to notified of primary route changes.
|
||||||
|
*
|
||||||
|
* You probably don't need to use this unless you're writing a reusable component.
|
||||||
|
*/
|
||||||
|
registerPrimaryOutlet(outlet: RouterOutlet): Promise<boolean> {
|
||||||
if (isPresent(outlet.name)) {
|
if (isPresent(outlet.name)) {
|
||||||
this._auxOutlets.set(outlet.name, outlet);
|
throw new BaseException(`registerAuxOutlet expects to be called with an unnamed outlet.`);
|
||||||
} else {
|
|
||||||
this._outlet = outlet;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._outlet = outlet;
|
||||||
if (isPresent(this._currentInstruction)) {
|
if (isPresent(this._currentInstruction)) {
|
||||||
return outlet.commit(this._currentInstruction);
|
return this.commit(this._currentInstruction, false);
|
||||||
}
|
}
|
||||||
return _resolveToTrue;
|
return _resolveToTrue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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<boolean> {
|
||||||
|
var outletName = outlet.name;
|
||||||
|
if (isBlank(outletName)) {
|
||||||
|
throw new BaseException(`registerAuxOutlet expects to be called with an outlet with a name.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO...
|
||||||
|
// what is the host of an aux route???
|
||||||
|
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 = this;
|
||||||
|
while (isPresent(router.parent) && isPresent(instruction.child)) {
|
||||||
|
router = router.parent;
|
||||||
|
instruction = instruction.child;
|
||||||
|
}
|
||||||
|
return isPresent(this._currentInstruction) &&
|
||||||
|
this._currentInstruction.component == instruction.component;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dynamically update the routing configuration and trigger a navigation.
|
* Dynamically update the routing configuration and trigger a navigation.
|
||||||
|
@ -143,7 +197,7 @@ export class Router {
|
||||||
|
|
||||||
_navigate(instruction: Instruction, _skipLocationChange: boolean): Promise<any> {
|
_navigate(instruction: Instruction, _skipLocationChange: boolean): Promise<any> {
|
||||||
return this._settleInstruction(instruction)
|
return this._settleInstruction(instruction)
|
||||||
.then((_) => this._reuse(instruction))
|
.then((_) => this._canReuse(instruction))
|
||||||
.then((_) => this._canActivate(instruction))
|
.then((_) => this._canActivate(instruction))
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
if (!result) {
|
if (!result) {
|
||||||
|
@ -190,14 +244,17 @@ export class Router {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_reuse(instruction: Instruction): Promise<any> {
|
/*
|
||||||
|
* Recursively set reuse flags
|
||||||
|
*/
|
||||||
|
_canReuse(instruction: Instruction): Promise<any> {
|
||||||
if (isBlank(this._outlet)) {
|
if (isBlank(this._outlet)) {
|
||||||
return _resolveToFalse;
|
return _resolveToFalse;
|
||||||
}
|
}
|
||||||
return this._outlet.canReuse(instruction)
|
return this._outlet.canReuse(instruction.component)
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
if (isPresent(this._outlet.childRouter) && isPresent(instruction.child)) {
|
if (isPresent(this._childRouter) && isPresent(instruction.child)) {
|
||||||
return this._outlet.childRouter._reuse(instruction.child);
|
return this._childRouter._canReuse(instruction.child);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -211,19 +268,26 @@ export class Router {
|
||||||
return _resolveToTrue;
|
return _resolveToTrue;
|
||||||
}
|
}
|
||||||
var next: Promise<boolean>;
|
var next: Promise<boolean>;
|
||||||
if (isPresent(instruction) && instruction.component.reuse) {
|
var childInstruction: Instruction = null;
|
||||||
|
var reuse: boolean = false;
|
||||||
|
var componentInstruction: ComponentInstruction = null;
|
||||||
|
if (isPresent(instruction)) {
|
||||||
|
childInstruction = instruction.child;
|
||||||
|
componentInstruction = instruction.component;
|
||||||
|
reuse = instruction.component.reuse;
|
||||||
|
}
|
||||||
|
if (reuse) {
|
||||||
next = _resolveToTrue;
|
next = _resolveToTrue;
|
||||||
} else {
|
} else {
|
||||||
next = this._outlet.canDeactivate(instruction);
|
next = this._outlet.canDeactivate(componentInstruction);
|
||||||
}
|
}
|
||||||
// TODO: aux route lifecycle hooks
|
// TODO: aux route lifecycle hooks
|
||||||
return next.then((result) => {
|
return next.then((result) => {
|
||||||
if (result == false) {
|
if (result == false) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (isPresent(this._outlet.childRouter)) {
|
if (isPresent(this._childRouter)) {
|
||||||
return this._outlet.childRouter._canDeactivate(isPresent(instruction) ? instruction.child :
|
return this._childRouter._canDeactivate(childInstruction);
|
||||||
null);
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
@ -234,13 +298,29 @@ export class Router {
|
||||||
*/
|
*/
|
||||||
commit(instruction: Instruction, _skipLocationChange: boolean = false): Promise<any> {
|
commit(instruction: Instruction, _skipLocationChange: boolean = false): Promise<any> {
|
||||||
this._currentInstruction = instruction;
|
this._currentInstruction = instruction;
|
||||||
var next = _resolveToTrue;
|
var next: Promise<any> = _resolveToTrue;
|
||||||
if (isPresent(this._outlet)) {
|
if (isPresent(this._outlet)) {
|
||||||
next = this._outlet.commit(instruction);
|
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 = [];
|
var promises = [];
|
||||||
MapWrapper.forEach(this._auxOutlets,
|
MapWrapper.forEach(this._auxRouters, (router, name) => {
|
||||||
(outlet, _) => { promises.push(outlet.commit(instruction)); });
|
promises.push(router.commit(instruction.auxInstruction[name]));
|
||||||
|
});
|
||||||
|
|
||||||
return next.then((_) => PromiseWrapper.all(promises));
|
return next.then((_) => PromiseWrapper.all(promises));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -262,10 +342,23 @@ export class Router {
|
||||||
* Removes the contents of this router's outlet and all descendant outlets
|
* Removes the contents of this router's outlet and all descendant outlets
|
||||||
*/
|
*/
|
||||||
deactivate(instruction: Instruction): Promise<any> {
|
deactivate(instruction: Instruction): Promise<any> {
|
||||||
if (isPresent(this._outlet)) {
|
var childInstruction: Instruction = null;
|
||||||
return this._outlet.deactivate(instruction);
|
var componentInstruction: ComponentInstruction = null;
|
||||||
|
if (isPresent(instruction)) {
|
||||||
|
childInstruction = instruction.child;
|
||||||
|
componentInstruction = instruction.component;
|
||||||
}
|
}
|
||||||
return _resolveToTrue;
|
var next: Promise<any> = _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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,19 @@
|
||||||
import {Promise, PromiseWrapper} from 'angular2/src/core/facade/async';
|
import {Promise, PromiseWrapper} from 'angular2/src/core/facade/async';
|
||||||
import {StringMapWrapper} from 'angular2/src/core/facade/collection';
|
import {StringMapWrapper} from 'angular2/src/core/facade/collection';
|
||||||
import {isBlank, isPresent} from 'angular2/src/core/facade/lang';
|
import {isBlank, isPresent, BaseException} from 'angular2/src/core/facade/lang';
|
||||||
|
|
||||||
import {Directive, Attribute} from '../core/metadata';
|
import {Directive, Attribute} from '../core/metadata';
|
||||||
import {DynamicComponentLoader, ComponentRef, ElementRef} from 'angular2/core';
|
import {DynamicComponentLoader, ComponentRef, ElementRef} from 'angular2/core';
|
||||||
import {Injector, bind, Dependency, UNDEFINED} from 'angular2/di';
|
import {Injector, bind, Dependency, UNDEFINED} from 'angular2/di';
|
||||||
|
|
||||||
import * as routerMod from './router';
|
import * as routerMod from './router';
|
||||||
import {Instruction, ComponentInstruction, RouteParams} from './instruction';
|
import {ComponentInstruction, RouteParams} from './instruction';
|
||||||
import {ROUTE_DATA} from './route_data';
|
import {ROUTE_DATA} from './route_data';
|
||||||
import * as hookMod from './lifecycle_annotations';
|
import * as hookMod from './lifecycle_annotations';
|
||||||
import {hasLifecycleHook} from './route_lifecycle_reflector';
|
import {hasLifecycleHook} from './route_lifecycle_reflector';
|
||||||
|
|
||||||
|
let _resolveToTrue = PromiseWrapper.resolve(true);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A router outlet is a placeholder that Angular dynamically fills based on the application's route.
|
* A router outlet is a placeholder that Angular dynamically fills based on the application's route.
|
||||||
*
|
*
|
||||||
|
@ -23,7 +25,6 @@ import {hasLifecycleHook} from './route_lifecycle_reflector';
|
||||||
*/
|
*/
|
||||||
@Directive({selector: 'router-outlet'})
|
@Directive({selector: 'router-outlet'})
|
||||||
export class RouterOutlet {
|
export class RouterOutlet {
|
||||||
childRouter: routerMod.Router = null;
|
|
||||||
name: string = null;
|
name: string = null;
|
||||||
|
|
||||||
private _componentRef: ComponentRef = null;
|
private _componentRef: ComponentRef = null;
|
||||||
|
@ -33,141 +34,96 @@ export class RouterOutlet {
|
||||||
private _parentRouter: routerMod.Router, @Attribute('name') nameAttr: string) {
|
private _parentRouter: routerMod.Router, @Attribute('name') nameAttr: string) {
|
||||||
if (isPresent(nameAttr)) {
|
if (isPresent(nameAttr)) {
|
||||||
this.name = nameAttr;
|
this.name = nameAttr;
|
||||||
}
|
this._parentRouter.registerAuxOutlet(this);
|
||||||
this._parentRouter.registerOutlet(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Given an instruction, update the contents of this outlet.
|
|
||||||
*/
|
|
||||||
commit(instruction: Instruction): Promise<any> {
|
|
||||||
instruction = this._getInstruction(instruction);
|
|
||||||
var componentInstruction = instruction.component;
|
|
||||||
if (isBlank(componentInstruction)) {
|
|
||||||
return PromiseWrapper.resolve(true);
|
|
||||||
}
|
|
||||||
var next;
|
|
||||||
if (componentInstruction.reuse) {
|
|
||||||
next = this._reuse(componentInstruction);
|
|
||||||
} else {
|
} else {
|
||||||
next = this.deactivate(instruction).then((_) => this._activate(componentInstruction));
|
this._parentRouter.registerPrimaryOutlet(this);
|
||||||
}
|
|
||||||
return next.then((_) => this._commitChild(instruction));
|
|
||||||
}
|
|
||||||
|
|
||||||
private _getInstruction(instruction: Instruction): Instruction {
|
|
||||||
if (isPresent(this.name)) {
|
|
||||||
return instruction.auxInstruction[this.name];
|
|
||||||
} else {
|
|
||||||
return instruction;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _commitChild(instruction: Instruction): Promise<any> {
|
activate(nextInstruction: ComponentInstruction): Promise<any> {
|
||||||
if (isPresent(this.childRouter)) {
|
|
||||||
return this.childRouter.commit(instruction.child);
|
|
||||||
} else {
|
|
||||||
return PromiseWrapper.resolve(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private _activate(instruction: ComponentInstruction): Promise<any> {
|
|
||||||
var previousInstruction = this._currentInstruction;
|
var previousInstruction = this._currentInstruction;
|
||||||
this._currentInstruction = instruction;
|
this._currentInstruction = nextInstruction;
|
||||||
var componentType = instruction.componentType;
|
var componentType = nextInstruction.componentType;
|
||||||
this.childRouter = this._parentRouter.childRouter(componentType);
|
var childRouter = this._parentRouter.childRouter(componentType);
|
||||||
|
|
||||||
var bindings = Injector.resolve([
|
var bindings = Injector.resolve([
|
||||||
bind(ROUTE_DATA)
|
bind(ROUTE_DATA)
|
||||||
.toValue(instruction.routeData()),
|
.toValue(nextInstruction.routeData()),
|
||||||
bind(RouteParams).toValue(new RouteParams(instruction.params)),
|
bind(RouteParams).toValue(new RouteParams(nextInstruction.params)),
|
||||||
bind(routerMod.Router).toValue(this.childRouter)
|
bind(routerMod.Router).toValue(childRouter)
|
||||||
]);
|
]);
|
||||||
return this._loader.loadNextToLocation(componentType, this._elementRef, bindings)
|
return this._loader.loadNextToLocation(componentType, this._elementRef, bindings)
|
||||||
.then((componentRef) => {
|
.then((componentRef) => {
|
||||||
this._componentRef = componentRef;
|
this._componentRef = componentRef;
|
||||||
if (hasLifecycleHook(hookMod.onActivate, componentType)) {
|
if (hasLifecycleHook(hookMod.onActivate, componentType)) {
|
||||||
return this._componentRef.instance.onActivate(instruction, previousInstruction);
|
return this._componentRef.instance.onActivate(nextInstruction, previousInstruction);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reuse(nextInstruction: ComponentInstruction): Promise<any> {
|
||||||
/**
|
|
||||||
* Called by Router during recognition phase
|
|
||||||
*/
|
|
||||||
canDeactivate(nextInstruction: Instruction): Promise<boolean> {
|
|
||||||
if (isBlank(this._currentInstruction)) {
|
|
||||||
return PromiseWrapper.resolve(true);
|
|
||||||
}
|
|
||||||
var outletInstruction = this._getInstruction(nextInstruction);
|
|
||||||
if (hasLifecycleHook(hookMod.canDeactivate, this._currentInstruction.componentType)) {
|
|
||||||
return PromiseWrapper.resolve(this._componentRef.instance.canDeactivate(
|
|
||||||
isPresent(outletInstruction) ? outletInstruction.component : null,
|
|
||||||
this._currentInstruction));
|
|
||||||
}
|
|
||||||
return PromiseWrapper.resolve(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called by Router during recognition phase
|
|
||||||
*/
|
|
||||||
canReuse(nextInstruction: Instruction): Promise<boolean> {
|
|
||||||
var result;
|
|
||||||
|
|
||||||
var outletInstruction = this._getInstruction(nextInstruction);
|
|
||||||
var componentInstruction = outletInstruction.component;
|
|
||||||
|
|
||||||
if (isBlank(this._currentInstruction) ||
|
|
||||||
this._currentInstruction.componentType != componentInstruction.componentType) {
|
|
||||||
result = false;
|
|
||||||
} else if (hasLifecycleHook(hookMod.canReuse, this._currentInstruction.componentType)) {
|
|
||||||
result = this._componentRef.instance.canReuse(componentInstruction, this._currentInstruction);
|
|
||||||
} else {
|
|
||||||
result =
|
|
||||||
componentInstruction == this._currentInstruction ||
|
|
||||||
(isPresent(componentInstruction.params) && isPresent(this._currentInstruction.params) &&
|
|
||||||
StringMapWrapper.equals(componentInstruction.params, this._currentInstruction.params));
|
|
||||||
}
|
|
||||||
return PromiseWrapper.resolve(result).then((result) => {
|
|
||||||
// TODO: this is a hack
|
|
||||||
componentInstruction.reuse = result;
|
|
||||||
return result;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private _reuse(instruction: ComponentInstruction): Promise<any> {
|
|
||||||
var previousInstruction = this._currentInstruction;
|
var previousInstruction = this._currentInstruction;
|
||||||
this._currentInstruction = instruction;
|
this._currentInstruction = nextInstruction;
|
||||||
|
|
||||||
|
if (isBlank(this._componentRef)) {
|
||||||
|
throw new BaseException(`Cannot reuse an outlet that does not contain a component.`);
|
||||||
|
}
|
||||||
return PromiseWrapper.resolve(
|
return PromiseWrapper.resolve(
|
||||||
hasLifecycleHook(hookMod.onReuse, this._currentInstruction.componentType) ?
|
hasLifecycleHook(hookMod.onReuse, this._currentInstruction.componentType) ?
|
||||||
this._componentRef.instance.onReuse(instruction, previousInstruction) :
|
this._componentRef.instance.onReuse(nextInstruction, previousInstruction) :
|
||||||
true);
|
true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
deactivate(nextInstruction: ComponentInstruction): Promise<any> {
|
||||||
|
var next = _resolveToTrue;
|
||||||
deactivate(nextInstruction: Instruction): Promise<any> {
|
|
||||||
var outletInstruction = this._getInstruction(nextInstruction);
|
|
||||||
var componentInstruction = isPresent(outletInstruction) ? outletInstruction.component : null;
|
|
||||||
return (isPresent(this.childRouter) ?
|
|
||||||
this.childRouter.deactivate(isPresent(outletInstruction) ? outletInstruction.child :
|
|
||||||
null) :
|
|
||||||
PromiseWrapper.resolve(true))
|
|
||||||
.then((_) => {
|
|
||||||
if (isPresent(this._componentRef) && isPresent(this._currentInstruction) &&
|
if (isPresent(this._componentRef) && isPresent(this._currentInstruction) &&
|
||||||
hasLifecycleHook(hookMod.onDeactivate, this._currentInstruction.componentType)) {
|
hasLifecycleHook(hookMod.onDeactivate, this._currentInstruction.componentType)) {
|
||||||
return this._componentRef.instance.onDeactivate(componentInstruction,
|
next = PromiseWrapper.resolve(
|
||||||
this._currentInstruction);
|
this._componentRef.instance.onDeactivate(nextInstruction, this._currentInstruction));
|
||||||
}
|
}
|
||||||
})
|
return next.then((_) => {
|
||||||
.then((_) => {
|
|
||||||
if (isPresent(this._componentRef)) {
|
if (isPresent(this._componentRef)) {
|
||||||
this._componentRef.dispose();
|
this._componentRef.dispose();
|
||||||
this._componentRef = null;
|
this._componentRef = null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by Router during recognition phase
|
||||||
|
*/
|
||||||
|
canDeactivate(nextInstruction: ComponentInstruction): Promise<boolean> {
|
||||||
|
if (isBlank(this._currentInstruction)) {
|
||||||
|
return _resolveToTrue;
|
||||||
|
}
|
||||||
|
if (hasLifecycleHook(hookMod.canDeactivate, this._currentInstruction.componentType)) {
|
||||||
|
return PromiseWrapper.resolve(
|
||||||
|
this._componentRef.instance.canDeactivate(nextInstruction, this._currentInstruction));
|
||||||
|
}
|
||||||
|
return _resolveToTrue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called by Router during recognition phase
|
||||||
|
*/
|
||||||
|
canReuse(nextInstruction: ComponentInstruction): Promise<boolean> {
|
||||||
|
var result;
|
||||||
|
|
||||||
|
if (isBlank(this._currentInstruction) ||
|
||||||
|
this._currentInstruction.componentType != nextInstruction.componentType) {
|
||||||
|
result = false;
|
||||||
|
} else if (hasLifecycleHook(hookMod.canReuse, this._currentInstruction.componentType)) {
|
||||||
|
result = this._componentRef.instance.canReuse(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).then((result) => {
|
||||||
|
// TODO: this is a hack
|
||||||
|
nextInstruction.reuse = result;
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,6 +105,7 @@ function makeDummyLocation() {
|
||||||
function makeDummyRouter() {
|
function makeDummyRouter() {
|
||||||
var dr = new SpyRouter();
|
var dr = new SpyRouter();
|
||||||
dr.spy('generate').andCallFake((routeParams) => dummyInstruction);
|
dr.spy('generate').andCallFake((routeParams) => dummyInstruction);
|
||||||
|
dr.spy('isRouteActive').andCallFake((_) => false);
|
||||||
dr.spy('navigateInstruction');
|
dr.spy('navigateInstruction');
|
||||||
return dr;
|
return dr;
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,9 +53,9 @@ export function main() {
|
||||||
var outlet = makeDummyOutlet();
|
var outlet = makeDummyOutlet();
|
||||||
|
|
||||||
router.config([new Route({path: '/', component: DummyComponent})])
|
router.config([new Route({path: '/', component: DummyComponent})])
|
||||||
.then((_) => router.registerOutlet(outlet))
|
.then((_) => router.registerPrimaryOutlet(outlet))
|
||||||
.then((_) => {
|
.then((_) => {
|
||||||
expect(outlet.spy('commit')).toHaveBeenCalled();
|
expect(outlet.spy('activate')).toHaveBeenCalled();
|
||||||
expect(location.urlChanges).toEqual([]);
|
expect(location.urlChanges).toEqual([]);
|
||||||
async.done();
|
async.done();
|
||||||
});
|
});
|
||||||
|
@ -66,11 +66,11 @@ export function main() {
|
||||||
inject([AsyncTestCompleter], (async) => {
|
inject([AsyncTestCompleter], (async) => {
|
||||||
var outlet = makeDummyOutlet();
|
var outlet = makeDummyOutlet();
|
||||||
|
|
||||||
router.registerOutlet(outlet)
|
router.registerPrimaryOutlet(outlet)
|
||||||
.then((_) => router.config([new Route({path: '/a', component: DummyComponent})]))
|
.then((_) => router.config([new Route({path: '/a', component: DummyComponent})]))
|
||||||
.then((_) => router.navigate('/a'))
|
.then((_) => router.navigate('/a'))
|
||||||
.then((_) => {
|
.then((_) => {
|
||||||
expect(outlet.spy('commit')).toHaveBeenCalled();
|
expect(outlet.spy('activate')).toHaveBeenCalled();
|
||||||
expect(location.urlChanges).toEqual(['/a']);
|
expect(location.urlChanges).toEqual(['/a']);
|
||||||
async.done();
|
async.done();
|
||||||
});
|
});
|
||||||
|
@ -80,11 +80,11 @@ export function main() {
|
||||||
inject([AsyncTestCompleter], (async) => {
|
inject([AsyncTestCompleter], (async) => {
|
||||||
var outlet = makeDummyOutlet();
|
var outlet = makeDummyOutlet();
|
||||||
|
|
||||||
router.registerOutlet(outlet)
|
router.registerPrimaryOutlet(outlet)
|
||||||
.then((_) => router.config([new Route({path: '/b', component: DummyComponent})]))
|
.then((_) => router.config([new Route({path: '/b', component: DummyComponent})]))
|
||||||
.then((_) => router.navigate('/b', true))
|
.then((_) => router.navigate('/b', true))
|
||||||
.then((_) => {
|
.then((_) => {
|
||||||
expect(outlet.spy('commit')).toHaveBeenCalled();
|
expect(outlet.spy('activate')).toHaveBeenCalled();
|
||||||
expect(location.urlChanges).toEqual([]);
|
expect(location.urlChanges).toEqual([]);
|
||||||
async.done();
|
async.done();
|
||||||
});
|
});
|
||||||
|
@ -94,14 +94,14 @@ export function main() {
|
||||||
it('should navigate after being configured', inject([AsyncTestCompleter], (async) => {
|
it('should navigate after being configured', inject([AsyncTestCompleter], (async) => {
|
||||||
var outlet = makeDummyOutlet();
|
var outlet = makeDummyOutlet();
|
||||||
|
|
||||||
router.registerOutlet(outlet)
|
router.registerPrimaryOutlet(outlet)
|
||||||
.then((_) => router.navigate('/a'))
|
.then((_) => router.navigate('/a'))
|
||||||
.then((_) => {
|
.then((_) => {
|
||||||
expect(outlet.spy('commit')).not.toHaveBeenCalled();
|
expect(outlet.spy('activate')).not.toHaveBeenCalled();
|
||||||
return router.config([new Route({path: '/a', component: DummyComponent})]);
|
return router.config([new Route({path: '/a', component: DummyComponent})]);
|
||||||
})
|
})
|
||||||
.then((_) => {
|
.then((_) => {
|
||||||
expect(outlet.spy('commit')).toHaveBeenCalled();
|
expect(outlet.spy('activate')).toHaveBeenCalled();
|
||||||
async.done();
|
async.done();
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
@ -142,13 +142,33 @@ export function main() {
|
||||||
inject([AsyncTestCompleter], (async) => {
|
inject([AsyncTestCompleter], (async) => {
|
||||||
var outlet = makeDummyOutlet();
|
var outlet = makeDummyOutlet();
|
||||||
|
|
||||||
router.registerOutlet(outlet);
|
router.registerPrimaryOutlet(outlet);
|
||||||
router.config([new AsyncRoute({path: '/first', loader: loader, as: 'FirstCmp'})]);
|
router.config([new AsyncRoute({path: '/first', loader: loader, as: 'FirstCmp'})]);
|
||||||
|
|
||||||
var instruction = router.generate(['/FirstCmp']);
|
var instruction = router.generate(['/FirstCmp']);
|
||||||
router.navigateInstruction(instruction)
|
router.navigateInstruction(instruction)
|
||||||
.then((_) => {
|
.then((_) => {
|
||||||
expect(outlet.spy('commit')).toHaveBeenCalled();
|
expect(outlet.spy('activate')).toHaveBeenCalled();
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should return whether a given instruction is active with isRouteActive',
|
||||||
|
inject([AsyncTestCompleter], (async) => {
|
||||||
|
var outlet = makeDummyOutlet();
|
||||||
|
|
||||||
|
router.registerPrimaryOutlet(outlet)
|
||||||
|
.then((_) => router.config([
|
||||||
|
new Route({path: '/a', component: DummyComponent, as: 'A'}),
|
||||||
|
new Route({path: '/b', component: DummyComponent, as: 'B'})
|
||||||
|
]))
|
||||||
|
.then((_) => router.navigate('/a'))
|
||||||
|
.then((_) => {
|
||||||
|
var instruction = router.generate(['/A']);
|
||||||
|
var otherInstruction = router.generate(['/B']);
|
||||||
|
|
||||||
|
expect(router.isRouteActive(instruction)).toEqual(true);
|
||||||
|
expect(router.isRouteActive(otherInstruction)).toEqual(false);
|
||||||
async.done();
|
async.done();
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
@ -213,7 +233,7 @@ function makeDummyOutlet() {
|
||||||
ref.spy('canActivate').andCallFake((_) => PromiseWrapper.resolve(true));
|
ref.spy('canActivate').andCallFake((_) => PromiseWrapper.resolve(true));
|
||||||
ref.spy('canReuse').andCallFake((_) => PromiseWrapper.resolve(false));
|
ref.spy('canReuse').andCallFake((_) => PromiseWrapper.resolve(false));
|
||||||
ref.spy('canDeactivate').andCallFake((_) => PromiseWrapper.resolve(true));
|
ref.spy('canDeactivate').andCallFake((_) => PromiseWrapper.resolve(true));
|
||||||
ref.spy('commit').andCallFake((_) => PromiseWrapper.resolve(true));
|
ref.spy('activate').andCallFake((_) => PromiseWrapper.resolve(true));
|
||||||
return ref;
|
return ref;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue