2016-04-22 12:05:38 -07:00
|
|
|
import {provide, ReflectiveInjector, ComponentResolver} from 'angular2/core';
|
|
|
|
|
import {RouterOutlet} from './directives/router_outlet';
|
|
|
|
|
import {Type, isBlank, isPresent} from 'angular2/src/facade/lang';
|
2016-04-25 16:57:27 -07:00
|
|
|
import {StringMapWrapper} from 'angular2/src/facade/collection';
|
|
|
|
|
import {BaseException} from 'angular2/src/facade/exceptions';
|
2016-04-22 12:05:38 -07:00
|
|
|
import {RouterUrlParser} from './router_url_parser';
|
|
|
|
|
import {recognize} from './recognize';
|
2016-04-25 16:57:27 -07:00
|
|
|
import {
|
|
|
|
|
equalSegments,
|
|
|
|
|
routeSegmentComponentFactory,
|
|
|
|
|
RouteSegment,
|
|
|
|
|
Tree,
|
|
|
|
|
rootNode,
|
|
|
|
|
TreeNode
|
|
|
|
|
} from './segments';
|
2016-04-22 12:05:38 -07:00
|
|
|
import {hasLifecycleHook} from './lifecycle_reflector';
|
2016-04-25 16:57:27 -07:00
|
|
|
import {DEFAULT_OUTLET_NAME} from './constants';
|
2016-04-22 12:05:38 -07:00
|
|
|
|
|
|
|
|
export class RouterOutletMap {
|
|
|
|
|
/** @internal */
|
|
|
|
|
_outlets: {[name: string]: RouterOutlet} = {};
|
|
|
|
|
registerOutlet(name: string, outlet: RouterOutlet): void { this._outlets[name] = outlet; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export class Router {
|
|
|
|
|
private prevTree: Tree<RouteSegment>;
|
|
|
|
|
constructor(private _componentType: Type, private _componentResolver: ComponentResolver,
|
|
|
|
|
private _urlParser: RouterUrlParser, private _routerOutletMap: RouterOutletMap) {}
|
|
|
|
|
|
|
|
|
|
navigateByUrl(url: string): Promise<void> {
|
2016-04-25 16:57:27 -07:00
|
|
|
let urlSegmentTree = this._urlParser.parse(url);
|
2016-04-22 12:05:38 -07:00
|
|
|
return recognize(this._componentResolver, this._componentType, urlSegmentTree)
|
|
|
|
|
.then(currTree => {
|
2016-04-25 16:57:27 -07:00
|
|
|
let prevRoot = isPresent(this.prevTree) ? rootNode(this.prevTree) : null;
|
|
|
|
|
new _SegmentLoader(currTree, this.prevTree)
|
|
|
|
|
.loadSegments(rootNode(currTree), prevRoot, this._routerOutletMap);
|
2016-04-22 12:05:38 -07:00
|
|
|
this.prevTree = currTree;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-25 16:57:27 -07:00
|
|
|
class _SegmentLoader {
|
|
|
|
|
constructor(private currTree: Tree<RouteSegment>, private prevTree: Tree<RouteSegment>) {}
|
2016-04-22 12:05:38 -07:00
|
|
|
|
2016-04-25 16:57:27 -07:00
|
|
|
loadSegments(currNode: TreeNode<RouteSegment>, prevNode: TreeNode<RouteSegment>,
|
|
|
|
|
parentOutletMap: RouterOutletMap): 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);
|
|
|
|
|
} else {
|
|
|
|
|
let outletMap = new RouterOutletMap();
|
|
|
|
|
this.loadNewSegment(outletMap, curr, prev, outlet);
|
|
|
|
|
this.loadChildSegments(currNode, prevNode, outletMap);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private loadNewSegment(outletMap: RouterOutletMap, curr: RouteSegment, prev: RouteSegment,
|
|
|
|
|
outlet: RouterOutlet): void {
|
2016-04-22 12:05:38 -07:00
|
|
|
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)) {
|
2016-04-25 16:57:27 -07:00
|
|
|
ref.instance.routerOnActivate(curr, prev, this.currTree, this.prevTree);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private loadChildSegments(currNode: TreeNode<RouteSegment>, prevNode: TreeNode<RouteSegment>,
|
|
|
|
|
outletMap: RouterOutletMap): 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);
|
|
|
|
|
StringMapWrapper.delete(prevChildren, c.value.outlet);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
StringMapWrapper.forEach(prevChildren, (v, k) => this.unloadOutlet(outletMap._outlets[k]));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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}`);
|
|
|
|
|
}
|
2016-04-22 12:05:38 -07:00
|
|
|
}
|
2016-04-25 16:57:27 -07:00
|
|
|
return outlet;
|
2016-04-22 12:05:38 -07:00
|
|
|
}
|
|
|
|
|
|
2016-04-25 16:57:27 -07:00
|
|
|
private unloadOutlet(outlet: RouterOutlet): void {
|
|
|
|
|
StringMapWrapper.forEach(outlet.outletMap._outlets, (v, k) => { this.unloadOutlet(v); });
|
|
|
|
|
outlet.unload();
|
2016-04-22 12:05:38 -07:00
|
|
|
}
|
|
|
|
|
}
|