parent
f5f85bb528
commit
a9a552c112
|
@ -24,7 +24,6 @@ export class Instruction {
|
|||
// "capturedUrl" is the part of the URL captured by this instruction
|
||||
// "accumulatedUrl" is the part of the URL captured by this instruction and all children
|
||||
accumulatedUrl: string;
|
||||
|
||||
reuse: boolean = false;
|
||||
specificity: number;
|
||||
|
||||
|
@ -50,23 +49,4 @@ export class Instruction {
|
|||
}
|
||||
return this._params;
|
||||
}
|
||||
|
||||
hasChild(): boolean { return isPresent(this.child); }
|
||||
|
||||
/**
|
||||
* Takes a currently active instruction and sets a reuse flag on each of this instruction's
|
||||
* children
|
||||
*/
|
||||
reuseComponentsFrom(oldInstruction: Instruction): void {
|
||||
var nextInstruction = this;
|
||||
while (nextInstruction.reuse = shouldReuseComponent(nextInstruction, oldInstruction) &&
|
||||
isPresent(oldInstruction = oldInstruction.child) &&
|
||||
isPresent(nextInstruction = nextInstruction.child))
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
function shouldReuseComponent(instr1: Instruction, instr2: Instruction): boolean {
|
||||
return instr1.component == instr2.component &&
|
||||
StringMapWrapper.equals(instr1.params(), instr2.params());
|
||||
}
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
import {Instruction} from './instruction';
|
||||
import {global} from 'angular2/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;
|
||||
|
||||
|
||||
/**
|
||||
* Defines route lifecycle method [onActivate]
|
||||
*/
|
||||
export interface OnActivate {
|
||||
onActivate(nextInstruction: Instruction, prevInstruction: Instruction): any;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines route lifecycle method [onReuse]
|
||||
*/
|
||||
export interface OnReuse {
|
||||
onReuse(nextInstruction: Instruction, prevInstruction: Instruction): any;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines route lifecycle method [onDeactivate]
|
||||
*/
|
||||
export interface OnDeactivate {
|
||||
onDeactivate(nextInstruction: Instruction, prevInstruction: Instruction): any;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines route lifecycle method [canReuse]
|
||||
*/
|
||||
export interface CanReuse {
|
||||
canReuse(nextInstruction: Instruction, prevInstruction: Instruction): any;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines route lifecycle method [canDeactivate]
|
||||
*/
|
||||
export interface CanDeactivate {
|
||||
canDeactivate(nextInstruction: Instruction, prevInstruction: Instruction): any;
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
/**
|
||||
* This indirection is needed for TS compilation path.
|
||||
* See comment in lifecycle_annotations.ts.
|
||||
*/
|
||||
|
||||
library angular2.router.lifecycle_annotations;
|
||||
|
||||
export "./lifecycle_annotations_impl.dart";
|
|
@ -0,0 +1,17 @@
|
|||
/**
|
||||
* This indirection is needed to free up Component, etc symbols in the public API
|
||||
* to be used by the decorator versions of these annotations.
|
||||
*/
|
||||
|
||||
import {makeDecorator} from 'angular2/src/util/decorators';
|
||||
import {CanActivate as CanActivateAnnotation} from './lifecycle_annotations_impl';
|
||||
|
||||
export {
|
||||
canReuse,
|
||||
canDeactivate,
|
||||
onActivate,
|
||||
onReuse,
|
||||
onDeactivate
|
||||
} from './lifecycle_annotations_impl';
|
||||
|
||||
export var CanActivate = makeDecorator(CanActivateAnnotation);
|
|
@ -0,0 +1,18 @@
|
|||
import {CONST, CONST_EXPR} from 'angular2/src/facade/lang';
|
||||
|
||||
@CONST()
|
||||
export class RouteLifecycleHook {
|
||||
constructor(public name: string) {}
|
||||
}
|
||||
|
||||
@CONST()
|
||||
export class CanActivate {
|
||||
constructor(public fn: Function) {}
|
||||
}
|
||||
|
||||
export const canReuse: RouteLifecycleHook = CONST_EXPR(new RouteLifecycleHook("canReuse"));
|
||||
export const canDeactivate: RouteLifecycleHook =
|
||||
CONST_EXPR(new RouteLifecycleHook("canDeactivate"));
|
||||
export const onActivate: RouteLifecycleHook = CONST_EXPR(new RouteLifecycleHook("onActivate"));
|
||||
export const onReuse: RouteLifecycleHook = CONST_EXPR(new RouteLifecycleHook("onReuse"));
|
||||
export const onDeactivate: RouteLifecycleHook = CONST_EXPR(new RouteLifecycleHook("onDeactivate"));
|
|
@ -0,0 +1,38 @@
|
|||
library angular.router.route_lifecycle_reflector;
|
||||
|
||||
import 'package:angular2/src/router/lifecycle_annotations_impl.dart';
|
||||
import 'package:angular2/src/router/interfaces.dart';
|
||||
import 'package:angular2/src/reflection/reflection.dart';
|
||||
|
||||
bool hasLifecycleHook(RouteLifecycleHook e, type) {
|
||||
if (type is! Type) return false;
|
||||
|
||||
final List interfaces = reflector.interfaces(type);
|
||||
var interface;
|
||||
|
||||
if (e == onActivate) {
|
||||
interface = OnActivate;
|
||||
} else if (e == onDeactivate) {
|
||||
interface = OnDeactivate;
|
||||
} else if (e == onReuse) {
|
||||
interface = OnReuse;
|
||||
} else if (e == canDeactivate) {
|
||||
interface = CanDeactivate;
|
||||
} else if (e == canReuse) {
|
||||
interface = CanReuse;
|
||||
}
|
||||
|
||||
return interfaces.contains(interface);
|
||||
}
|
||||
|
||||
Function getCanActivateHook(type) {
|
||||
final List annotations = reflector.annotations(type);
|
||||
|
||||
for (var annotation in annotations) {
|
||||
if (annotation is CanActivate) {
|
||||
return annotation.fn;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
import {Type, isPresent} from 'angular2/src/facade/lang';
|
||||
import {RouteLifecycleHook, CanActivate} from './lifecycle_annotations_impl';
|
||||
import {reflector} from 'angular2/src/reflection/reflection';
|
||||
|
||||
export function hasLifecycleHook(e: RouteLifecycleHook, type): boolean {
|
||||
if (!(type instanceof Type)) return false;
|
||||
return e.name in(<any>type).prototype;
|
||||
}
|
||||
|
||||
export function getCanActivateHook(type): Function {
|
||||
var annotations = reflector.annotations(type);
|
||||
for (let i = 0; i < annotations.length; i += 1) {
|
||||
let annotation = annotations[i];
|
||||
if (annotation instanceof CanActivate) {
|
||||
return annotation.fn;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
|
@ -126,7 +126,11 @@ export class RouteRegistry {
|
|||
this.configFromComponent(componentType);
|
||||
|
||||
if (partialMatch.unmatchedUrl.length == 0) {
|
||||
return new Instruction(componentType, partialMatch.matchedUrl, recognizer);
|
||||
if (recognizer.terminal) {
|
||||
return new Instruction(componentType, partialMatch.matchedUrl, recognizer);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return this.recognize(partialMatch.unmatchedUrl, componentType)
|
||||
|
|
|
@ -15,6 +15,7 @@ import {Pipeline} from './pipeline';
|
|||
import {Instruction} from './instruction';
|
||||
import {RouterOutlet} from './router_outlet';
|
||||
import {Location} from './location';
|
||||
import {getCanActivateHook} from './route_lifecycle_reflector';
|
||||
|
||||
let _resolveToTrue = PromiseWrapper.resolve(true);
|
||||
let _resolveToFalse = PromiseWrapper.resolve(false);
|
||||
|
@ -39,7 +40,6 @@ let _resolveToFalse = PromiseWrapper.resolve(false);
|
|||
export class Router {
|
||||
navigating: boolean = false;
|
||||
lastNavigationAttempt: string;
|
||||
previousUrl: string = null;
|
||||
|
||||
private _currentInstruction: Instruction = null;
|
||||
private _currentNavigation: Promise<any> = _resolveToTrue;
|
||||
|
@ -67,7 +67,7 @@ export class Router {
|
|||
// TODO: sibling routes
|
||||
this._outlet = outlet;
|
||||
if (isPresent(this._currentInstruction)) {
|
||||
return outlet.activate(this._currentInstruction);
|
||||
return outlet.commit(this._currentInstruction);
|
||||
}
|
||||
return _resolveToTrue;
|
||||
}
|
||||
|
@ -109,35 +109,94 @@ export class Router {
|
|||
* If the given URL does not begin with `/`, the router will navigate relative to this component.
|
||||
*/
|
||||
navigate(url: string): Promise<any> {
|
||||
if (this.navigating) {
|
||||
return this._currentNavigation;
|
||||
}
|
||||
this.lastNavigationAttempt = url;
|
||||
return this._currentNavigation = this.recognize(url).then((matchedInstruction) => {
|
||||
if (isBlank(matchedInstruction)) {
|
||||
return _resolveToFalse;
|
||||
}
|
||||
|
||||
if (isPresent(this._currentInstruction)) {
|
||||
matchedInstruction.reuseComponentsFrom(this._currentInstruction);
|
||||
}
|
||||
|
||||
return this._currentNavigation = this._currentNavigation.then((_) => {
|
||||
this.lastNavigationAttempt = url;
|
||||
this._startNavigating();
|
||||
|
||||
var result =
|
||||
this.commit(matchedInstruction)
|
||||
.then((_) => {
|
||||
this._finishNavigating();
|
||||
ObservableWrapper.callNext(this._subject, matchedInstruction.accumulatedUrl);
|
||||
});
|
||||
|
||||
return PromiseWrapper.catchError(result, (err) => {
|
||||
this._finishNavigating();
|
||||
throw err;
|
||||
});
|
||||
return this._afterPromiseFinishNavigating(this.recognize(url).then((matchedInstruction) => {
|
||||
if (isBlank(matchedInstruction)) {
|
||||
return false;
|
||||
}
|
||||
return this._reuse(matchedInstruction)
|
||||
.then((_) => this._canActivate(matchedInstruction))
|
||||
.then((result) => {
|
||||
if (!result) {
|
||||
return false;
|
||||
}
|
||||
return this._canDeactivate(matchedInstruction)
|
||||
.then((result) => {
|
||||
if (result) {
|
||||
return this.commit(matchedInstruction)
|
||||
.then((_) => {
|
||||
this._emitNavigationFinish(matchedInstruction.accumulatedUrl);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
private _emitNavigationFinish(url): void { ObservableWrapper.callNext(this._subject, url); }
|
||||
|
||||
private _afterPromiseFinishNavigating(promise: Promise<any>): Promise<any> {
|
||||
return PromiseWrapper.catchError(promise.then((_) => this._finishNavigating()), (err) => {
|
||||
this._finishNavigating();
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
|
||||
_reuse(instruction): Promise<any> {
|
||||
if (isBlank(this._outlet)) {
|
||||
return _resolveToFalse;
|
||||
}
|
||||
return this._outlet.canReuse(instruction)
|
||||
.then((result) => {
|
||||
instruction.reuse = result;
|
||||
if (isPresent(this._outlet.childRouter) && isPresent(instruction.child)) {
|
||||
return this._outlet.childRouter._reuse(instruction.child);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private _canActivate(instruction: Instruction): Promise<boolean> {
|
||||
return canActivateOne(instruction, this._currentInstruction);
|
||||
}
|
||||
|
||||
private _canDeactivate(instruction: Instruction): Promise<boolean> {
|
||||
if (isBlank(this._outlet)) {
|
||||
return _resolveToTrue;
|
||||
}
|
||||
var next: Promise<boolean>;
|
||||
if (isPresent(instruction) && instruction.reuse) {
|
||||
next = _resolveToTrue;
|
||||
} else {
|
||||
next = this._outlet.canDeactivate(instruction);
|
||||
}
|
||||
return next.then((result) => {
|
||||
if (result == false) {
|
||||
return false;
|
||||
}
|
||||
if (isPresent(this._outlet.childRouter)) {
|
||||
return this._outlet.childRouter._canDeactivate(isPresent(instruction) ? instruction.child :
|
||||
null);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates this router and all descendant routers according to the given instruction
|
||||
*/
|
||||
commit(instruction: Instruction): Promise<any> {
|
||||
this._currentInstruction = instruction;
|
||||
if (isPresent(this._outlet)) {
|
||||
return this._outlet.commit(instruction);
|
||||
}
|
||||
return _resolveToTrue;
|
||||
}
|
||||
|
||||
|
||||
_startNavigating(): void { this.navigating = true; }
|
||||
|
||||
_finishNavigating(): void { this.navigating = false; }
|
||||
|
@ -149,24 +208,12 @@ export class Router {
|
|||
subscribe(onNext): void { ObservableWrapper.subscribe(this._subject, onNext); }
|
||||
|
||||
|
||||
/**
|
||||
* Updates this router and all descendant routers according to the given instruction
|
||||
*/
|
||||
commit(instruction: Instruction): Promise<any> {
|
||||
this._currentInstruction = instruction;
|
||||
if (isPresent(this._outlet)) {
|
||||
return this._outlet.activate(instruction);
|
||||
}
|
||||
return _resolveToTrue;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes the contents of this router's outlet and all descendant outlets
|
||||
*/
|
||||
deactivate(): Promise<any> {
|
||||
deactivate(instruction: Instruction): Promise<any> {
|
||||
if (isPresent(this._outlet)) {
|
||||
return this._outlet.deactivate();
|
||||
return this._outlet.deactivate(instruction);
|
||||
}
|
||||
return _resolveToTrue;
|
||||
}
|
||||
|
@ -185,11 +232,10 @@ export class Router {
|
|||
* router has yet to successfully navigate.
|
||||
*/
|
||||
renavigate(): Promise<any> {
|
||||
var destination = isBlank(this.previousUrl) ? this.lastNavigationAttempt : this.previousUrl;
|
||||
if (isBlank(destination)) {
|
||||
if (isBlank(this.lastNavigationAttempt)) {
|
||||
return this._currentNavigation;
|
||||
}
|
||||
return this.navigate(destination);
|
||||
return this.navigate(this.lastNavigationAttempt);
|
||||
}
|
||||
|
||||
|
||||
|
@ -288,3 +334,24 @@ function splitAndFlattenLinkParams(linkParams: List<any>): List<any> {
|
|||
return accumulation;
|
||||
}, []);
|
||||
}
|
||||
|
||||
function canActivateOne(nextInstruction, currentInstruction): Promise<boolean> {
|
||||
var next = _resolveToTrue;
|
||||
if (isPresent(nextInstruction.child)) {
|
||||
next = canActivateOne(nextInstruction.child,
|
||||
isPresent(currentInstruction) ? currentInstruction.child : null);
|
||||
}
|
||||
return next.then((res) => {
|
||||
if (res == false) {
|
||||
return false;
|
||||
}
|
||||
if (nextInstruction.reuse) {
|
||||
return true;
|
||||
}
|
||||
var hook = getCanActivateHook(nextInstruction.component);
|
||||
if (isPresent(hook)) {
|
||||
return hook(nextInstruction, currentInstruction);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
|
||||
import {StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
import {isBlank, isPresent} from 'angular2/src/facade/lang';
|
||||
|
||||
import {Directive, Attribute} from 'angular2/src/core/annotations/decorators';
|
||||
|
@ -6,8 +7,9 @@ import {DynamicComponentLoader, ComponentRef, ElementRef} from 'angular2/core';
|
|||
import {Injector, bind, Dependency, undefinedValue} from 'angular2/di';
|
||||
|
||||
import * as routerMod from './router';
|
||||
import {Instruction, RouteParams} from './instruction'
|
||||
|
||||
import {Instruction, RouteParams} from './instruction';
|
||||
import * as hookMod from './lifecycle_annotations';
|
||||
import {hasLifecycleHook} from './route_lifecycle_reflector';
|
||||
|
||||
/**
|
||||
* A router outlet is a placeholder that Angular dynamically fills based on the application's route.
|
||||
|
@ -18,11 +20,10 @@ import {Instruction, RouteParams} from './instruction'
|
|||
* <router-outlet></router-outlet>
|
||||
* ```
|
||||
*/
|
||||
@Directive({
|
||||
selector: 'router-outlet'
|
||||
})
|
||||
@Directive({selector: 'router-outlet'})
|
||||
export class RouterOutlet {
|
||||
private _childRouter: routerMod.Router = null;
|
||||
childRouter: routerMod.Router = null;
|
||||
|
||||
private _componentRef: ComponentRef = null;
|
||||
private _currentInstruction: Instruction = null;
|
||||
|
||||
|
@ -38,34 +39,100 @@ export class RouterOutlet {
|
|||
/**
|
||||
* Given an instruction, update the contents of this outlet.
|
||||
*/
|
||||
activate(instruction: Instruction): Promise<any> {
|
||||
// if we're able to reuse the component, we just have to pass along the instruction to the
|
||||
// component's router
|
||||
// so it can propagate changes to its children
|
||||
if ((instruction == this._currentInstruction || instruction.reuse) &&
|
||||
isPresent(this._childRouter)) {
|
||||
return this._childRouter.commit(instruction.child);
|
||||
commit(instruction: Instruction): Promise<any> {
|
||||
var next;
|
||||
if (instruction.reuse) {
|
||||
next = this._reuse(instruction);
|
||||
} else {
|
||||
next = this.deactivate(instruction).then((_) => this._activate(instruction));
|
||||
}
|
||||
return next.then((_) => this._commitChild(instruction));
|
||||
}
|
||||
|
||||
private _commitChild(instruction: Instruction): Promise<any> {
|
||||
if (isPresent(this.childRouter)) {
|
||||
return this.childRouter.commit(instruction.child);
|
||||
} else {
|
||||
return PromiseWrapper.resolve(true);
|
||||
}
|
||||
}
|
||||
|
||||
private _activate(instruction: Instruction): Promise<any> {
|
||||
var previousInstruction = this._currentInstruction;
|
||||
this._currentInstruction = instruction;
|
||||
this._childRouter = this._parentRouter.childRouter(instruction.component);
|
||||
var params = new RouteParams(instruction.params());
|
||||
var bindings = Injector.resolve(
|
||||
[bind(RouteParams).toValue(params), bind(routerMod.Router).toValue(this._childRouter)]);
|
||||
this.childRouter = this._parentRouter.childRouter(instruction.component);
|
||||
|
||||
return this.deactivate()
|
||||
.then((_) => this._loader.loadNextToLocation(instruction.component, this._elementRef,
|
||||
bindings))
|
||||
var bindings = Injector.resolve([
|
||||
bind(RouteParams)
|
||||
.toValue(new RouteParams(instruction.params())),
|
||||
bind(routerMod.Router).toValue(this.childRouter)
|
||||
]);
|
||||
return this._loader.loadNextToLocation(instruction.component, this._elementRef, bindings)
|
||||
.then((componentRef) => {
|
||||
this._componentRef = componentRef;
|
||||
return this._childRouter.commit(instruction.child);
|
||||
if (hasLifecycleHook(hookMod.onActivate, instruction.component)) {
|
||||
return this._componentRef.instance.onActivate(instruction, previousInstruction);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
deactivate(): Promise<any> {
|
||||
return (isPresent(this._childRouter) ? this._childRouter.deactivate() :
|
||||
PromiseWrapper.resolve(true))
|
||||
/**
|
||||
* Called by Router during recognition phase
|
||||
*/
|
||||
canDeactivate(nextInstruction: Instruction): Promise<boolean> {
|
||||
if (isBlank(this._currentInstruction)) {
|
||||
return PromiseWrapper.resolve(true);
|
||||
}
|
||||
if (hasLifecycleHook(hookMod.canDeactivate, this._currentInstruction.component)) {
|
||||
return PromiseWrapper.resolve(
|
||||
this._componentRef.instance.canDeactivate(nextInstruction, this._currentInstruction));
|
||||
}
|
||||
return PromiseWrapper.resolve(true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called by Router during recognition phase
|
||||
*/
|
||||
canReuse(nextInstruction: Instruction): Promise<boolean> {
|
||||
var result;
|
||||
if (isBlank(this._currentInstruction) ||
|
||||
this._currentInstruction.component != nextInstruction.component) {
|
||||
result = false;
|
||||
} else if (hasLifecycleHook(hookMod.canReuse, this._currentInstruction.component)) {
|
||||
result = this._componentRef.instance.canReuse(nextInstruction, this._currentInstruction);
|
||||
} else {
|
||||
result = nextInstruction == this._currentInstruction ||
|
||||
StringMapWrapper.equals(nextInstruction.params(), this._currentInstruction.params());
|
||||
}
|
||||
return PromiseWrapper.resolve(result);
|
||||
}
|
||||
|
||||
|
||||
private _reuse(instruction): Promise<any> {
|
||||
var previousInstruction = this._currentInstruction;
|
||||
this._currentInstruction = instruction;
|
||||
return PromiseWrapper.resolve(
|
||||
hasLifecycleHook(hookMod.onReuse, this._currentInstruction.component) ?
|
||||
this._componentRef.instance.onReuse(instruction, previousInstruction) :
|
||||
true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
deactivate(nextInstruction: Instruction): Promise<any> {
|
||||
return (isPresent(this.childRouter) ?
|
||||
this.childRouter.deactivate(isPresent(nextInstruction) ? nextInstruction.child :
|
||||
null) :
|
||||
PromiseWrapper.resolve(true))
|
||||
.then((_) => {
|
||||
if (isPresent(this._componentRef) && isPresent(this._currentInstruction) &&
|
||||
hasLifecycleHook(hookMod.onDeactivate, this._currentInstruction.component)) {
|
||||
return this._componentRef.instance.onDeactivate(nextInstruction,
|
||||
this._currentInstruction);
|
||||
}
|
||||
})
|
||||
.then((_) => {
|
||||
if (isPresent(this._componentRef)) {
|
||||
this._componentRef.dispose();
|
||||
|
@ -73,9 +140,4 @@ export class RouterOutlet {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
canDeactivate(instruction: Instruction): Promise<boolean> {
|
||||
// TODO: how to get ahold of the component instance here?
|
||||
return PromiseWrapper.resolve(true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,8 @@ import {
|
|||
import {Injector, bind} from 'angular2/di';
|
||||
import {Component, View} from 'angular2/src/core/annotations/decorators';
|
||||
import * as annotations from 'angular2/src/core/annotations_impl/view';
|
||||
import {CONST, NumberWrapper} from 'angular2/src/facade/lang';
|
||||
import {CONST, NumberWrapper, isPresent} from 'angular2/src/facade/lang';
|
||||
import {Promise, PromiseWrapper, EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
|
||||
|
||||
import {RootRouter} from 'angular2/src/router/router';
|
||||
import {Pipeline} from 'angular2/src/router/pipeline';
|
||||
|
@ -30,9 +31,18 @@ import {DOM} from 'angular2/src/dom/dom_adapter';
|
|||
import {SpyLocation} from 'angular2/src/mock/location_mock';
|
||||
import {Location} from 'angular2/src/router/location';
|
||||
import {RouteRegistry} from 'angular2/src/router/route_registry';
|
||||
import {
|
||||
OnActivate,
|
||||
OnDeactivate,
|
||||
OnReuse,
|
||||
CanDeactivate,
|
||||
CanReuse
|
||||
} from 'angular2/src/router/interfaces';
|
||||
import {CanActivate} from 'angular2/src/router/lifecycle_annotations';
|
||||
import {Instruction} from 'angular2/src/router/instruction';
|
||||
import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
|
||||
|
||||
var teamCmpCount;
|
||||
var cmpInstanceCount, log, eventBus, completer;
|
||||
|
||||
export function main() {
|
||||
describe('Outlet Directive', () => {
|
||||
|
@ -55,7 +65,9 @@ export function main() {
|
|||
tcb = tcBuilder;
|
||||
rtr = router;
|
||||
location = loc;
|
||||
teamCmpCount = 0;
|
||||
cmpInstanceCount = 0;
|
||||
log = '';
|
||||
eventBus = new EventEmitter();
|
||||
}));
|
||||
|
||||
function compile(template: string = "<router-outlet></router-outlet>") {
|
||||
|
@ -158,13 +170,13 @@ export function main() {
|
|||
.then((_) => rtr.navigate('/team/angular/user/rado'))
|
||||
.then((_) => {
|
||||
rootTC.detectChanges();
|
||||
expect(teamCmpCount).toBe(1);
|
||||
expect(cmpInstanceCount).toBe(1);
|
||||
expect(rootTC.nativeElement).toHaveText('team angular { hello rado }');
|
||||
})
|
||||
.then((_) => rtr.navigate('/team/angular/user/victor'))
|
||||
.then((_) => {
|
||||
rootTC.detectChanges();
|
||||
expect(teamCmpCount).toBe(1);
|
||||
expect(cmpInstanceCount).toBe(1);
|
||||
expect(rootTC.nativeElement).toHaveText('team angular { hello victor }');
|
||||
async.done();
|
||||
});
|
||||
|
@ -228,6 +240,282 @@ export function main() {
|
|||
});
|
||||
}));
|
||||
|
||||
it('should call the onActivate hook', inject([AsyncTestCompleter], (async) => {
|
||||
compile()
|
||||
.then((_) => rtr.config({'path': '/...', 'component': LifecycleCmp}))
|
||||
.then((_) => rtr.navigate('/on-activate'))
|
||||
.then((_) => {
|
||||
rootTC.detectChanges();
|
||||
expect(rootTC.nativeElement).toHaveText('activate cmp');
|
||||
expect(log).toEqual('activate: null -> /on-activate;');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should wait for a parent component\'s onActivate hook to resolve before calling its child\'s',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
compile()
|
||||
.then((_) => rtr.config({'path': '/...', 'component': LifecycleCmp}))
|
||||
.then((_) => {
|
||||
ObservableWrapper.subscribe(eventBus, (ev) => {
|
||||
if (ev.startsWith('parent activate')) {
|
||||
completer.resolve(true);
|
||||
}
|
||||
});
|
||||
rtr.navigate('/parent-activate/child-activate')
|
||||
.then((_) => {
|
||||
rootTC.detectChanges();
|
||||
expect(rootTC.nativeElement).toHaveText('parent {activate cmp}');
|
||||
expect(log).toEqual(
|
||||
'parent activate: null -> /parent-activate/child-activate;activate: null -> /child-activate;');
|
||||
async.done();
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
it('should call the onDeactivate hook', inject([AsyncTestCompleter], (async) => {
|
||||
compile()
|
||||
.then((_) => rtr.config({'path': '/...', 'component': LifecycleCmp}))
|
||||
.then((_) => rtr.navigate('/on-deactivate'))
|
||||
.then((_) => rtr.navigate('/a'))
|
||||
.then((_) => {
|
||||
rootTC.detectChanges();
|
||||
expect(rootTC.nativeElement).toHaveText('A');
|
||||
expect(log).toEqual('deactivate: /on-deactivate -> /a;');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should wait for a child component\'s onDeactivate hook to resolve before calling its parent\'s',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
compile()
|
||||
.then((_) => rtr.config({'path': '/...', 'component': LifecycleCmp}))
|
||||
.then((_) => rtr.navigate('/parent-deactivate/child-deactivate'))
|
||||
.then((_) => {
|
||||
ObservableWrapper.subscribe(eventBus, (ev) => {
|
||||
if (ev.startsWith('deactivate')) {
|
||||
completer.resolve(true);
|
||||
rootTC.detectChanges();
|
||||
expect(rootTC.nativeElement).toHaveText('parent {deactivate cmp}');
|
||||
}
|
||||
});
|
||||
rtr.navigate('/a').then((_) => {
|
||||
rootTC.detectChanges();
|
||||
expect(rootTC.nativeElement).toHaveText('A');
|
||||
expect(log).toEqual(
|
||||
'deactivate: /child-deactivate -> null;parent deactivate: /parent-deactivate/child-deactivate -> /a;');
|
||||
async.done();
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
it('should reuse a component when the canReuse hook returns false',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
compile()
|
||||
.then((_) => rtr.config({'path': '/...', 'component': LifecycleCmp}))
|
||||
.then((_) => rtr.navigate('/on-reuse/1/a'))
|
||||
.then((_) => {
|
||||
rootTC.detectChanges();
|
||||
expect(log).toEqual('');
|
||||
expect(rootTC.nativeElement).toHaveText('reuse {A}');
|
||||
expect(cmpInstanceCount).toBe(1);
|
||||
})
|
||||
.then((_) => rtr.navigate('/on-reuse/2/b'))
|
||||
.then((_) => {
|
||||
rootTC.detectChanges();
|
||||
expect(log).toEqual('reuse: /on-reuse/1/a -> /on-reuse/2/b;');
|
||||
expect(rootTC.nativeElement).toHaveText('reuse {B}');
|
||||
expect(cmpInstanceCount).toBe(1);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
|
||||
it('should not reuse a component when the canReuse hook returns false',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
compile()
|
||||
.then((_) => rtr.config({'path': '/...', 'component': LifecycleCmp}))
|
||||
.then((_) => rtr.navigate('/never-reuse/1/a'))
|
||||
.then((_) => {
|
||||
rootTC.detectChanges();
|
||||
expect(log).toEqual('');
|
||||
expect(rootTC.nativeElement).toHaveText('reuse {A}');
|
||||
expect(cmpInstanceCount).toBe(1);
|
||||
})
|
||||
.then((_) => rtr.navigate('/never-reuse/2/b'))
|
||||
.then((_) => {
|
||||
rootTC.detectChanges();
|
||||
expect(log).toEqual('');
|
||||
expect(rootTC.nativeElement).toHaveText('reuse {B}');
|
||||
expect(cmpInstanceCount).toBe(2);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should navigate when canActivate returns true', inject([AsyncTestCompleter], (async) => {
|
||||
compile()
|
||||
.then((_) => rtr.config({'path': '/...', 'component': LifecycleCmp}))
|
||||
.then((_) => {
|
||||
ObservableWrapper.subscribe(eventBus, (ev) => {
|
||||
if (ev.startsWith('canActivate')) {
|
||||
completer.resolve(true);
|
||||
}
|
||||
});
|
||||
rtr.navigate('/can-activate/a')
|
||||
.then((_) => {
|
||||
rootTC.detectChanges();
|
||||
expect(rootTC.nativeElement).toHaveText('canActivate {A}');
|
||||
expect(log).toEqual('canActivate: null -> /can-activate/a;');
|
||||
async.done();
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
it('should not navigate when canActivate returns false',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
compile()
|
||||
.then((_) => rtr.config({'path': '/...', 'component': LifecycleCmp}))
|
||||
.then((_) => {
|
||||
ObservableWrapper.subscribe(eventBus, (ev) => {
|
||||
if (ev.startsWith('canActivate')) {
|
||||
completer.resolve(false);
|
||||
}
|
||||
});
|
||||
rtr.navigate('/can-activate/a')
|
||||
.then((_) => {
|
||||
rootTC.detectChanges();
|
||||
expect(rootTC.nativeElement).toHaveText('');
|
||||
expect(log).toEqual('canActivate: null -> /can-activate/a;');
|
||||
async.done();
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
it('should navigate away when canDeactivate returns true',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
compile()
|
||||
.then((_) => rtr.config({'path': '/...', 'component': LifecycleCmp}))
|
||||
.then((_) => rtr.navigate('/can-deactivate/a'))
|
||||
.then((_) => {
|
||||
rootTC.detectChanges();
|
||||
expect(rootTC.nativeElement).toHaveText('canDeactivate {A}');
|
||||
expect(log).toEqual('');
|
||||
|
||||
ObservableWrapper.subscribe(eventBus, (ev) => {
|
||||
if (ev.startsWith('canDeactivate')) {
|
||||
completer.resolve(true);
|
||||
}
|
||||
});
|
||||
|
||||
rtr.navigate('/a').then((_) => {
|
||||
rootTC.detectChanges();
|
||||
expect(rootTC.nativeElement).toHaveText('A');
|
||||
expect(log).toEqual('canDeactivate: /can-deactivate/a -> /a;');
|
||||
async.done();
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
it('should not navigate away when canDeactivate returns false',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
compile()
|
||||
.then((_) => rtr.config({'path': '/...', 'component': LifecycleCmp}))
|
||||
.then((_) => rtr.navigate('/can-deactivate/a'))
|
||||
.then((_) => {
|
||||
rootTC.detectChanges();
|
||||
expect(rootTC.nativeElement).toHaveText('canDeactivate {A}');
|
||||
expect(log).toEqual('');
|
||||
|
||||
ObservableWrapper.subscribe(eventBus, (ev) => {
|
||||
if (ev.startsWith('canDeactivate')) {
|
||||
completer.resolve(false);
|
||||
}
|
||||
});
|
||||
|
||||
rtr.navigate('/a').then((_) => {
|
||||
rootTC.detectChanges();
|
||||
expect(rootTC.nativeElement).toHaveText('canDeactivate {A}');
|
||||
expect(log).toEqual('canDeactivate: /can-deactivate/a -> /a;');
|
||||
async.done();
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
|
||||
it('should run activation and deactivation hooks in the correct order',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
compile()
|
||||
.then((_) => rtr.config({'path': '/...', 'component': LifecycleCmp}))
|
||||
.then((_) => rtr.navigate('/activation-hooks/child'))
|
||||
.then((_) => {
|
||||
expect(log).toEqual('canActivate child: null -> /child;' +
|
||||
'canActivate parent: null -> /activation-hooks/child;' +
|
||||
'onActivate parent: null -> /activation-hooks/child;' +
|
||||
'onActivate child: null -> /child;');
|
||||
|
||||
log = '';
|
||||
return rtr.navigate('/a');
|
||||
})
|
||||
.then((_) => {
|
||||
expect(log).toEqual('canDeactivate parent: /activation-hooks/child -> /a;' +
|
||||
'canDeactivate child: /child -> null;' +
|
||||
'onDeactivate child: /child -> null;' +
|
||||
'onDeactivate parent: /activation-hooks/child -> /a;');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should only run reuse hooks when reusing', inject([AsyncTestCompleter], (async) => {
|
||||
compile()
|
||||
.then((_) => rtr.config({'path': '/...', 'component': LifecycleCmp}))
|
||||
.then((_) => rtr.navigate('/reuse-hooks/1'))
|
||||
.then((_) => {
|
||||
expect(log).toEqual('canActivate: null -> /reuse-hooks/1;' +
|
||||
'onActivate: null -> /reuse-hooks/1;');
|
||||
|
||||
ObservableWrapper.subscribe(eventBus, (ev) => {
|
||||
if (ev.startsWith('canReuse')) {
|
||||
completer.resolve(true);
|
||||
}
|
||||
});
|
||||
|
||||
log = '';
|
||||
return rtr.navigate('/reuse-hooks/2');
|
||||
})
|
||||
.then((_) => {
|
||||
expect(log).toEqual('canReuse: /reuse-hooks/1 -> /reuse-hooks/2;' +
|
||||
'onReuse: /reuse-hooks/1 -> /reuse-hooks/2;');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should not run reuse hooks when not reusing', inject([AsyncTestCompleter], (async) => {
|
||||
compile()
|
||||
.then((_) => rtr.config({'path': '/...', 'component': LifecycleCmp}))
|
||||
.then((_) => rtr.navigate('/reuse-hooks/1'))
|
||||
.then((_) => {
|
||||
expect(log).toEqual('canActivate: null -> /reuse-hooks/1;' +
|
||||
'onActivate: null -> /reuse-hooks/1;');
|
||||
|
||||
ObservableWrapper.subscribe(eventBus, (ev) => {
|
||||
if (ev.startsWith('canReuse')) {
|
||||
completer.resolve(false);
|
||||
}
|
||||
});
|
||||
|
||||
log = '';
|
||||
return rtr.navigate('/reuse-hooks/2');
|
||||
})
|
||||
.then((_) => {
|
||||
expect(log).toEqual('canReuse: /reuse-hooks/1 -> /reuse-hooks/2;' +
|
||||
'canActivate: /reuse-hooks/1 -> /reuse-hooks/2;' +
|
||||
'canDeactivate: /reuse-hooks/1 -> /reuse-hooks/2;' +
|
||||
'onDeactivate: /reuse-hooks/1 -> /reuse-hooks/2;' +
|
||||
'onActivate: /reuse-hooks/1 -> /reuse-hooks/2;');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
describe('when clicked', () => {
|
||||
|
||||
var clickOnElement = function(view) {
|
||||
|
@ -352,7 +640,7 @@ class TeamCmp {
|
|||
id: string;
|
||||
constructor(params: RouteParams) {
|
||||
this.id = params.get('id');
|
||||
teamCmpCount += 1;
|
||||
cmpInstanceCount += 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -361,3 +649,175 @@ class TeamCmp {
|
|||
class MyComp {
|
||||
name;
|
||||
}
|
||||
|
||||
function logHook(name: string, next: Instruction, prev: Instruction) {
|
||||
var message = name + ': ' + (isPresent(prev) ? prev.accumulatedUrl : 'null') + ' -> ' +
|
||||
(isPresent(next) ? next.accumulatedUrl : 'null') + ';';
|
||||
log += message;
|
||||
ObservableWrapper.callNext(eventBus, message);
|
||||
}
|
||||
|
||||
@Component({selector: 'activate-cmp'})
|
||||
@View({template: 'activate cmp'})
|
||||
class ActivateCmp implements OnActivate {
|
||||
onActivate(next: Instruction, prev: Instruction) { logHook('activate', next, prev); }
|
||||
}
|
||||
|
||||
@Component({selector: 'parent-activate-cmp'})
|
||||
@View({template: `parent {<router-outlet></router-outlet>}`, directives: [RouterOutlet]})
|
||||
@RouteConfig([{path: '/child-activate', component: ActivateCmp}])
|
||||
class ParentActivateCmp implements OnActivate {
|
||||
onActivate(next: Instruction, prev: Instruction): Promise<any> {
|
||||
completer = PromiseWrapper.completer();
|
||||
logHook('parent activate', next, prev);
|
||||
return completer.promise;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({selector: 'deactivate-cmp'})
|
||||
@View({template: 'deactivate cmp'})
|
||||
class DeactivateCmp implements OnDeactivate {
|
||||
onDeactivate(next: Instruction, prev: Instruction) { logHook('deactivate', next, prev); }
|
||||
}
|
||||
|
||||
@Component({selector: 'deactivate-cmp'})
|
||||
@View({template: 'deactivate cmp'})
|
||||
class WaitDeactivateCmp implements OnDeactivate {
|
||||
onDeactivate(next: Instruction, prev: Instruction): Promise<any> {
|
||||
completer = PromiseWrapper.completer();
|
||||
logHook('deactivate', next, prev);
|
||||
return completer.promise;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({selector: 'parent-deactivate-cmp'})
|
||||
@View({template: `parent {<router-outlet></router-outlet>}`, directives: [RouterOutlet]})
|
||||
@RouteConfig([{path: '/child-deactivate', component: WaitDeactivateCmp}])
|
||||
class ParentDeactivateCmp implements OnDeactivate {
|
||||
onDeactivate(next: Instruction, prev: Instruction) { logHook('parent deactivate', next, prev); }
|
||||
}
|
||||
|
||||
@Component({selector: 'reuse-cmp'})
|
||||
@View({template: `reuse {<router-outlet></router-outlet>}`, directives: [RouterOutlet]})
|
||||
@RouteConfig([{path: '/a', component: A}, {path: '/b', component: B}])
|
||||
class ReuseCmp implements OnReuse, CanReuse {
|
||||
constructor() { cmpInstanceCount += 1; }
|
||||
canReuse(next: Instruction, prev: Instruction) { return true; }
|
||||
onReuse(next: Instruction, prev: Instruction) { logHook('reuse', next, prev); }
|
||||
}
|
||||
|
||||
@Component({selector: 'never-reuse-cmp'})
|
||||
@View({template: `reuse {<router-outlet></router-outlet>}`, directives: [RouterOutlet]})
|
||||
@RouteConfig([{path: '/a', component: A}, {path: '/b', component: B}])
|
||||
class NeverReuseCmp implements OnReuse, CanReuse {
|
||||
constructor() { cmpInstanceCount += 1; }
|
||||
canReuse(next: Instruction, prev: Instruction) { return false; }
|
||||
onReuse(next: Instruction, prev: Instruction) { logHook('reuse', next, prev); }
|
||||
}
|
||||
|
||||
@Component({selector: 'can-activate-cmp'})
|
||||
@View({template: `canActivate {<router-outlet></router-outlet>}`, directives: [RouterOutlet]})
|
||||
@RouteConfig([{path: '/a', component: A}, {path: '/b', component: B}])
|
||||
@CanActivate(CanActivateCmp.canActivate)
|
||||
class CanActivateCmp {
|
||||
static canActivate(next: Instruction, prev: Instruction) {
|
||||
completer = PromiseWrapper.completer();
|
||||
logHook('canActivate', next, prev);
|
||||
return completer.promise;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({selector: 'can-deactivate-cmp'})
|
||||
@View({template: `canDeactivate {<router-outlet></router-outlet>}`, directives: [RouterOutlet]})
|
||||
@RouteConfig([{path: '/a', component: A}, {path: '/b', component: B}])
|
||||
class CanDeactivateCmp implements CanDeactivate {
|
||||
canDeactivate(next: Instruction, prev: Instruction) {
|
||||
completer = PromiseWrapper.completer();
|
||||
logHook('canDeactivate', next, prev);
|
||||
return completer.promise;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({selector: 'all-hooks-child-cmp'})
|
||||
@View({template: `child`})
|
||||
@CanActivate(AllHooksChildCmp.canActivate)
|
||||
class AllHooksChildCmp implements CanDeactivate, OnDeactivate, OnActivate {
|
||||
canDeactivate(next: Instruction, prev: Instruction) {
|
||||
logHook('canDeactivate child', next, prev);
|
||||
return true;
|
||||
}
|
||||
|
||||
onDeactivate(next: Instruction, prev: Instruction) { logHook('onDeactivate child', next, prev); }
|
||||
|
||||
static canActivate(next: Instruction, prev: Instruction) {
|
||||
logHook('canActivate child', next, prev);
|
||||
return true;
|
||||
}
|
||||
|
||||
onActivate(next: Instruction, prev: Instruction) { logHook('onActivate child', next, prev); }
|
||||
}
|
||||
|
||||
@Component({selector: 'all-hooks-parent-cmp'})
|
||||
@View({template: `<router-outlet></router-outlet>`, directives: [RouterOutlet]})
|
||||
@RouteConfig([{path: '/child', component: AllHooksChildCmp}])
|
||||
@CanActivate(AllHooksParentCmp.canActivate)
|
||||
class AllHooksParentCmp implements CanDeactivate, OnDeactivate, OnActivate {
|
||||
canDeactivate(next: Instruction, prev: Instruction) {
|
||||
logHook('canDeactivate parent', next, prev);
|
||||
return true;
|
||||
}
|
||||
|
||||
onDeactivate(next: Instruction, prev: Instruction) { logHook('onDeactivate parent', next, prev); }
|
||||
|
||||
static canActivate(next: Instruction, prev: Instruction) {
|
||||
logHook('canActivate parent', next, prev);
|
||||
return true;
|
||||
}
|
||||
|
||||
onActivate(next: Instruction, prev: Instruction) { logHook('onActivate parent', next, prev); }
|
||||
}
|
||||
|
||||
@Component({selector: 'reuse-hooks-cmp'})
|
||||
@View({template: 'reuse hooks cmp'})
|
||||
@CanActivate(ReuseHooksCmp.canActivate)
|
||||
class ReuseHooksCmp implements OnActivate, OnReuse, OnDeactivate, CanReuse, CanDeactivate {
|
||||
canReuse(next: Instruction, prev: Instruction): Promise<any> {
|
||||
completer = PromiseWrapper.completer();
|
||||
logHook('canReuse', next, prev);
|
||||
return completer.promise;
|
||||
}
|
||||
|
||||
onReuse(next: Instruction, prev: Instruction) { logHook('onReuse', next, prev); }
|
||||
|
||||
canDeactivate(next: Instruction, prev: Instruction) {
|
||||
logHook('canDeactivate', next, prev);
|
||||
return true;
|
||||
}
|
||||
|
||||
onDeactivate(next: Instruction, prev: Instruction) { logHook('onDeactivate', next, prev); }
|
||||
|
||||
static canActivate(next: Instruction, prev: Instruction) {
|
||||
logHook('canActivate', next, prev);
|
||||
return true;
|
||||
}
|
||||
|
||||
onActivate(next: Instruction, prev: Instruction) { logHook('onActivate', next, prev); }
|
||||
}
|
||||
|
||||
@Component({selector: 'lifecycle-cmp'})
|
||||
@View({template: `<router-outlet></router-outlet>`, directives: [RouterOutlet]})
|
||||
@RouteConfig([
|
||||
{path: '/a', component: A},
|
||||
{path: '/on-activate', component: ActivateCmp},
|
||||
{path: '/parent-activate/...', component: ParentActivateCmp},
|
||||
{path: '/on-deactivate', component: DeactivateCmp},
|
||||
{path: '/parent-deactivate/...', component: ParentDeactivateCmp},
|
||||
{path: '/on-reuse/:number/...', component: ReuseCmp},
|
||||
{path: '/never-reuse/:number/...', component: NeverReuseCmp},
|
||||
{path: '/can-activate/...', component: CanActivateCmp},
|
||||
{path: '/can-deactivate/...', component: CanDeactivateCmp},
|
||||
{path: '/activation-hooks/...', component: AllHooksParentCmp},
|
||||
{path: '/reuse-hooks/:number', component: ReuseHooksCmp}
|
||||
])
|
||||
class LifecycleCmp {
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ export function main() {
|
|||
router.config({'path': '/', 'component': DummyComponent})
|
||||
.then((_) => router.registerOutlet(outlet))
|
||||
.then((_) => {
|
||||
expect(outlet.spy('activate')).toHaveBeenCalled();
|
||||
expect(outlet.spy('commit')).toHaveBeenCalled();
|
||||
expect(location.urlChanges).toEqual([]);
|
||||
async.done();
|
||||
});
|
||||
|
@ -70,7 +70,7 @@ export function main() {
|
|||
.then((_) => router.config({'path': '/a', 'component': DummyComponent}))
|
||||
.then((_) => router.navigate('/a'))
|
||||
.then((_) => {
|
||||
expect(outlet.spy('activate')).toHaveBeenCalled();
|
||||
expect(outlet.spy('commit')).toHaveBeenCalled();
|
||||
expect(location.urlChanges).toEqual(['/a']);
|
||||
async.done();
|
||||
});
|
||||
|
@ -83,11 +83,11 @@ export function main() {
|
|||
router.registerOutlet(outlet)
|
||||
.then((_) => router.navigate('/a'))
|
||||
.then((_) => {
|
||||
expect(outlet.spy('activate')).not.toHaveBeenCalled();
|
||||
expect(outlet.spy('commit')).not.toHaveBeenCalled();
|
||||
return router.config({'path': '/a', 'component': DummyComponent});
|
||||
})
|
||||
.then((_) => {
|
||||
expect(outlet.spy('activate')).toHaveBeenCalled();
|
||||
expect(outlet.spy('commit')).toHaveBeenCalled();
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
@ -132,10 +132,10 @@ class DummyParentComp {
|
|||
|
||||
function makeDummyOutlet() {
|
||||
var ref = new DummyOutlet();
|
||||
ref.spy('activate').andCallFake((_) => PromiseWrapper.resolve(true));
|
||||
ref.spy('canActivate').andCallFake((_) => PromiseWrapper.resolve(true));
|
||||
ref.spy('canReuse').andCallFake((_) => PromiseWrapper.resolve(false));
|
||||
ref.spy('canDeactivate').andCallFake((_) => PromiseWrapper.resolve(true));
|
||||
ref.spy('deactivate').andCallFake((_) => PromiseWrapper.resolve(true));
|
||||
ref.spy('commit').andCallFake((_) => PromiseWrapper.resolve(true));
|
||||
return ref;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue