132 lines
5.0 KiB
TypeScript
Raw Normal View History

import {provide, ReflectiveInjector, ComponentResolver} from 'angular2/core';
import {RouterOutlet} from './directives/router_outlet';
import {Type, isBlank, isPresent} from 'angular2/src/facade/lang';
2016-04-27 15:37:20 -07:00
import {EventEmitter, Observable} from 'angular2/src/facade/async';
import {StringMapWrapper} from 'angular2/src/facade/collection';
import {BaseException} from 'angular2/src/facade/exceptions';
import {BaseException} from 'angular2/src/facade/exceptions';
2016-04-27 15:37:20 -07:00
import {RouterUrlSerializer} from './router_url_serializer';
import {recognize} from './recognize';
import {Location} from './location/location';
import {
equalSegments,
routeSegmentComponentFactory,
RouteSegment,
Tree,
rootNode,
2016-04-27 15:37:20 -07:00
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 {
2016-04-27 15:37:20 -07:00
private _prevTree: Tree<RouteSegment>;
private _urlTree: Tree<UrlSegment>;
private _location: Location;
2016-04-27 15:37:20 -07:00
private _changes: EventEmitter<void> = new EventEmitter<void>();
constructor(private _componentType: Type, private _componentResolver: ComponentResolver,
2016-04-27 15:37:20 -07:00
private _urlSerializer: RouterUrlSerializer,
private _routerOutletMap: RouterOutletMap, location: Location) {
this._location = location;
this.navigateByUrl(location.path());
}
2016-04-27 15:37:20 -07:00
get urlTree(): Tree<UrlSegment> { return this._urlTree; }
navigate(url: Tree<UrlSegment>): Promise<void> {
this._urlTree = url;
return recognize(this._componentResolver, this._componentType, url)
.then(currTree => {
2016-04-27 15:37:20 -07:00
let prevRoot = isPresent(this._prevTree) ? rootNode(this._prevTree) : null;
new _LoadSegments(currTree, this._prevTree)
.loadSegments(rootNode(currTree), prevRoot, this._routerOutletMap);
2016-04-27 15:37:20 -07:00
this._prevTree = currTree;
this._location.go(this._urlSerializer.serialize(this._urlTree));
2016-04-27 15:37:20 -07:00
this._changes.emit(null);
});
}
2016-04-27 15:37:20 -07:00
serializeUrl(url: Tree<UrlSegment>): string { return this._urlSerializer.serialize(url); }
navigateByUrl(url: string): Promise<void> {
return this.navigate(this._urlSerializer.parse(url));
}
get changes(): Observable<void> { return this._changes; }
}
2016-04-27 15:37:20 -07:00
class _LoadSegments {
constructor(private currTree: Tree<RouteSegment>, private prevTree: Tree<RouteSegment>) {}
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 {
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);
}
}
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}`);
}
}
return outlet;
}
private unloadOutlet(outlet: RouterOutlet): void {
StringMapWrapper.forEach(outlet.outletMap._outlets, (v, k) => { this.unloadOutlet(v); });
outlet.unload();
}
}