refactor(router): add types
This commit is contained in:
parent
3644036693
commit
fc13cdab3a
|
@ -4,21 +4,22 @@ export class BrowserLocation {
|
|||
_location;
|
||||
_history;
|
||||
_baseHref:string;
|
||||
|
||||
constructor() {
|
||||
this._location = DOM.getLocation();
|
||||
this._history = DOM.getHistory();
|
||||
this._baseHref = DOM.getBaseHref();
|
||||
}
|
||||
|
||||
onPopState(fn) {
|
||||
onPopState(fn: Function): void {
|
||||
DOM.getGlobalEventTarget('window').addEventListener('popstate', fn, false);
|
||||
}
|
||||
|
||||
getBaseHref() {
|
||||
getBaseHref(): string {
|
||||
return this._baseHref;
|
||||
}
|
||||
|
||||
path() {
|
||||
path(): string {
|
||||
return this._location.pathname;
|
||||
}
|
||||
|
||||
|
@ -26,11 +27,11 @@ export class BrowserLocation {
|
|||
this._history.pushState(state, title, url);
|
||||
}
|
||||
|
||||
forward() {
|
||||
forward(): void {
|
||||
this._history.forward();
|
||||
}
|
||||
|
||||
back() {
|
||||
back(): void {
|
||||
this._history.back();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,28 +1,29 @@
|
|||
import {Map, MapWrapper, StringMap, StringMapWrapper, List, ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
|
||||
import {isPresent} from 'angular2/src/facade/lang';
|
||||
import {isPresent, normalizeBlank} from 'angular2/src/facade/lang';
|
||||
|
||||
export class RouteParams {
|
||||
params:Map<string, string>;
|
||||
params:StringMap<string, string>;
|
||||
|
||||
constructor(params:StringMap) {
|
||||
this.params = params;
|
||||
}
|
||||
|
||||
get(param:string) {
|
||||
return StringMapWrapper.get(this.params, param);
|
||||
get(param:string): string {
|
||||
return normalizeBlank(StringMapWrapper.get(this.params, param));
|
||||
}
|
||||
}
|
||||
|
||||
export class Instruction {
|
||||
component:any;
|
||||
_children:Map<string, Instruction>;
|
||||
_children:StringMap<string, Instruction>;
|
||||
router:any;
|
||||
matchedUrl:string;
|
||||
params:Map<string, string>;
|
||||
params:StringMap<string, string>;
|
||||
reuse:boolean;
|
||||
cost:number;
|
||||
|
||||
constructor({params, component, children, matchedUrl, parentCost}:{params:StringMap, component:any, children:Map, matchedUrl:string, cost:int} = {}) {
|
||||
constructor({params, component, children, matchedUrl, parentCost}:{params:StringMap, component:any, children:StringMap, matchedUrl:string, cost:number} = {}) {
|
||||
this.reuse = false;
|
||||
this.matchedUrl = matchedUrl;
|
||||
this.cost = parentCost;
|
||||
|
@ -43,11 +44,11 @@ export class Instruction {
|
|||
this.params = params;
|
||||
}
|
||||
|
||||
getChildInstruction(outletName:string) {
|
||||
getChildInstruction(outletName:string): Instruction {
|
||||
return StringMapWrapper.get(this._children, outletName);
|
||||
}
|
||||
|
||||
forEachChild(fn:Function) {
|
||||
forEachChild(fn:Function): void {
|
||||
StringMapWrapper.forEach(this._children, fn);
|
||||
}
|
||||
|
||||
|
@ -60,7 +61,7 @@ export class Instruction {
|
|||
* Takes a function with signature:
|
||||
* (parent:Instruction, child:Instruction) => {}
|
||||
*/
|
||||
traverseSync(fn:Function) {
|
||||
traverseSync(fn:Function): void {
|
||||
this.forEachChild((childInstruction, _) => fn(this, childInstruction));
|
||||
this.forEachChild((childInstruction, _) => childInstruction.traverseSync(fn));
|
||||
}
|
||||
|
@ -70,7 +71,7 @@ export class Instruction {
|
|||
* Takes a function with signature:
|
||||
* (child:Instruction, parentOutletName:string) => {}
|
||||
*/
|
||||
traverseAsync(fn:Function) {
|
||||
traverseAsync(fn:Function):Promise {
|
||||
return this.mapChildrenAsync(fn)
|
||||
.then((_) => this.mapChildrenAsync((childInstruction, _) => childInstruction.traverseAsync(fn)));
|
||||
}
|
||||
|
@ -79,7 +80,7 @@ export class Instruction {
|
|||
/**
|
||||
* Takes a currently active instruction and sets a reuse flag on this instruction
|
||||
*/
|
||||
reuseComponentsFrom(oldInstruction:Instruction) {
|
||||
reuseComponentsFrom(oldInstruction:Instruction): void {
|
||||
this.forEachChild((childInstruction, outletName) => {
|
||||
var oldInstructionChild = oldInstruction.getChildInstruction(outletName);
|
||||
if (shouldReuseComponent(childInstruction, oldInstructionChild)) {
|
||||
|
@ -89,16 +90,16 @@ export class Instruction {
|
|||
}
|
||||
}
|
||||
|
||||
function shouldReuseComponent(instr1:Instruction, instr2:Instruction) {
|
||||
function shouldReuseComponent(instr1:Instruction, instr2:Instruction): boolean {
|
||||
return instr1.component == instr2.component &&
|
||||
StringMapWrapper.equals(instr1.params, instr2.params);
|
||||
}
|
||||
|
||||
function mapObjAsync(obj:StringMap, fn) {
|
||||
function mapObjAsync(obj:StringMap, fn): Promise {
|
||||
return PromiseWrapper.all(mapObj(obj, fn));
|
||||
}
|
||||
|
||||
function mapObj(obj:StringMap, fn):List {
|
||||
function mapObj(obj:StringMap, fn: Function):List {
|
||||
var result = ListWrapper.create();
|
||||
StringMapWrapper.forEach(obj, (value, key) => ListWrapper.push(result, fn(value, key)));
|
||||
return result;
|
||||
|
|
|
@ -13,62 +13,62 @@ export class Location {
|
|||
this._browserLocation.onPopState((_) => this._onPopState(_));
|
||||
}
|
||||
|
||||
_onPopState(_) {
|
||||
_onPopState(_): void {
|
||||
ObservableWrapper.callNext(this._subject, {
|
||||
'url': this.path()
|
||||
});
|
||||
}
|
||||
|
||||
path() {
|
||||
path(): string {
|
||||
return this.normalize(this._browserLocation.path());
|
||||
}
|
||||
|
||||
normalize(url) {
|
||||
normalize(url: string): string {
|
||||
return this._stripBaseHref(stripIndexHtml(url));
|
||||
}
|
||||
|
||||
normalizeAbsolutely(url) {
|
||||
normalizeAbsolutely(url: string): string {
|
||||
if (url[0] != '/') {
|
||||
url = '/' + url;
|
||||
}
|
||||
return this._addBaseHref(url);
|
||||
}
|
||||
|
||||
_stripBaseHref(url) {
|
||||
_stripBaseHref(url: string): string {
|
||||
if (this._baseHref.length > 0 && StringWrapper.startsWith(url, this._baseHref)) {
|
||||
return StringWrapper.substring(url, this._baseHref.length);
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
_addBaseHref(url) {
|
||||
_addBaseHref(url: string): string {
|
||||
if (!StringWrapper.startsWith(url, this._baseHref)) {
|
||||
return this._baseHref + url;
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
go(url:string) {
|
||||
go(url:string): void {
|
||||
var finalUrl = this.normalizeAbsolutely(url);
|
||||
this._browserLocation.pushState(null, '', finalUrl);
|
||||
}
|
||||
|
||||
forward() {
|
||||
forward(): void {
|
||||
this._browserLocation.forward();
|
||||
}
|
||||
|
||||
back() {
|
||||
back(): void {
|
||||
this._browserLocation.back();
|
||||
}
|
||||
|
||||
subscribe(onNext, onThrow = null, onReturn = null) {
|
||||
subscribe(onNext, onThrow = null, onReturn = null): void {
|
||||
ObservableWrapper.subscribe(this._subject, onNext, onThrow, onReturn);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
function stripIndexHtml(url) {
|
||||
function stripIndexHtml(url: string): string {
|
||||
// '/index.html'.length == 11
|
||||
if (url.length > 10 && StringWrapper.substring(url, url.length - 11) == '/index.html') {
|
||||
return StringWrapper.substring(url, 0, url.length - 11);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {RegExp, RegExpWrapper, RegExpMatcherWrapper, StringWrapper, isPresent, isBlank, BaseException} from 'angular2/src/facade/lang';
|
||||
import {RegExp, RegExpWrapper, RegExpMatcherWrapper, StringWrapper, isPresent, isBlank, BaseException, normalizeBlank} from 'angular2/src/facade/lang';
|
||||
import {Map, MapWrapper, StringMap, StringMapWrapper, List, ListWrapper} from 'angular2/src/facade/collection';
|
||||
|
||||
import {escapeRegex} from './url';
|
||||
|
@ -7,13 +7,14 @@ class StaticSegment {
|
|||
string:string;
|
||||
regex:string;
|
||||
name:string;
|
||||
|
||||
constructor(string:string) {
|
||||
this.string = string;
|
||||
this.name = '';
|
||||
this.regex = escapeRegex(string);
|
||||
}
|
||||
|
||||
generate(params) {
|
||||
generate(params): string {
|
||||
return this.string;
|
||||
}
|
||||
}
|
||||
|
@ -26,11 +27,11 @@ class DynamicSegment {
|
|||
this.regex = "([^/]+)";
|
||||
}
|
||||
|
||||
generate(params:StringMap) {
|
||||
generate(params:StringMap<string, string>): string {
|
||||
if (!StringMapWrapper.contains(params, this.name)) {
|
||||
throw new BaseException(`Route generator for '${this.name}' was not included in parameters passed.`)
|
||||
}
|
||||
return StringMapWrapper.get(params, this.name);
|
||||
return normalizeBlank(StringMapWrapper.get(params, this.name));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -43,8 +44,8 @@ class StarSegment {
|
|||
this.regex = "(.+)";
|
||||
}
|
||||
|
||||
generate(params:StringMap) {
|
||||
return StringMapWrapper.get(params, this.name);
|
||||
generate(params:StringMap<string, string>): string {
|
||||
return normalizeBlank(StringMapWrapper.get(params, this.name));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -82,9 +83,8 @@ function parsePathString(route:string) {
|
|||
return {segments: results, cost};
|
||||
}
|
||||
|
||||
var SLASH_RE = RegExpWrapper.create('/');
|
||||
function splitBySlash (url:string):List<string> {
|
||||
return StringWrapper.split(url, SLASH_RE);
|
||||
return url.split('/');
|
||||
}
|
||||
|
||||
|
||||
|
@ -97,7 +97,7 @@ export class PathRecognizer {
|
|||
|
||||
constructor(path:string, handler:any) {
|
||||
this.handler = handler;
|
||||
this.segments = ListWrapper.create();
|
||||
this.segments = [];
|
||||
|
||||
// TODO: use destructuring assignment
|
||||
// see https://github.com/angular/ts2dart/issues/158
|
||||
|
@ -115,7 +115,7 @@ export class PathRecognizer {
|
|||
this.cost = cost;
|
||||
}
|
||||
|
||||
parseParams(url:string):StringMap {
|
||||
parseParams(url:string):StringMap<string, string> {
|
||||
var params = StringMapWrapper.create();
|
||||
var urlPart = url;
|
||||
for(var i=0; i<this.segments.length; i++) {
|
||||
|
@ -130,7 +130,7 @@ export class PathRecognizer {
|
|||
return params;
|
||||
}
|
||||
|
||||
generate(params:StringMap):string {
|
||||
generate(params:StringMap<string, string>):string {
|
||||
return ListWrapper.join(ListWrapper.map(this.segments, (segment) =>
|
||||
'/' + segment.generate(params)), '');
|
||||
}
|
||||
|
|
|
@ -7,7 +7,8 @@ import {Instruction} from './instruction';
|
|||
* "Steps" are conceptually similar to "middleware"
|
||||
*/
|
||||
export class Pipeline {
|
||||
steps:List;
|
||||
steps:List<Function>;
|
||||
|
||||
constructor() {
|
||||
this.steps = [
|
||||
instruction => instruction.traverseSync((parentInstruction, childInstruction) => {
|
||||
|
|
|
@ -11,6 +11,7 @@ import {List, Map} from 'angular2/src/facade/collection';
|
|||
*/
|
||||
export class RouteConfig {
|
||||
configs:List<Map>;
|
||||
|
||||
@CONST()
|
||||
constructor(configs:List<Map>) {
|
||||
this.configs = configs;
|
||||
|
|
|
@ -14,11 +14,11 @@ export class RouteRecognizer {
|
|||
this.redirects = MapWrapper.create();
|
||||
}
|
||||
|
||||
addRedirect(path:string, target:string) {
|
||||
addRedirect(path:string, target:string): void {
|
||||
MapWrapper.set(this.redirects, path, target);
|
||||
}
|
||||
|
||||
addConfig(path:string, handler:any, alias:string = null) {
|
||||
addConfig(path:string, handler:any, alias:string = null): void {
|
||||
var recognizer = new PathRecognizer(path, handler);
|
||||
MapWrapper.set(this.matchers, recognizer.regex, recognizer);
|
||||
if (isPresent(alias)) {
|
||||
|
@ -59,12 +59,12 @@ export class RouteRecognizer {
|
|||
return solutions;
|
||||
}
|
||||
|
||||
hasRoute(name:string) {
|
||||
hasRoute(name:string): boolean {
|
||||
return MapWrapper.contains(this.names, name);
|
||||
}
|
||||
|
||||
generate(name:string, params:any) {
|
||||
generate(name:string, params:any): string {
|
||||
var pathRecognizer = MapWrapper.get(this.names, name);
|
||||
return pathRecognizer.generate(params);
|
||||
return isPresent(pathRecognizer) ? pathRecognizer.generate(params) : null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ export class RouteRegistry {
|
|||
this._rules = MapWrapper.create();
|
||||
}
|
||||
|
||||
config(parentComponent, config) {
|
||||
config(parentComponent, config: StringMap<string, any>): void {
|
||||
if (!StringMapWrapper.contains(config, 'path')) {
|
||||
throw new BaseException('Route config does not contain "path"');
|
||||
}
|
||||
|
@ -23,10 +23,9 @@ export class RouteRegistry {
|
|||
throw new BaseException('Route config does not contain "component," "components," or "redirectTo"');
|
||||
}
|
||||
|
||||
var recognizer:RouteRecognizer;
|
||||
if (MapWrapper.contains(this._rules, parentComponent)) {
|
||||
recognizer = MapWrapper.get(this._rules, parentComponent);
|
||||
} else {
|
||||
var recognizer:RouteRecognizer = MapWrapper.get(this._rules, parentComponent);
|
||||
|
||||
if (isBlank(recognizer)) {
|
||||
recognizer = new RouteRecognizer();
|
||||
MapWrapper.set(this._rules, parentComponent, recognizer);
|
||||
}
|
||||
|
@ -46,7 +45,7 @@ export class RouteRegistry {
|
|||
recognizer.addConfig(config['path'], config, config['as']);
|
||||
}
|
||||
|
||||
configFromComponent(component) {
|
||||
configFromComponent(component): void {
|
||||
if (!isType(component)) {
|
||||
return;
|
||||
}
|
||||
|
@ -71,14 +70,14 @@ export class RouteRegistry {
|
|||
}
|
||||
|
||||
|
||||
recognize(url:string, parentComponent) {
|
||||
recognize(url:string, parentComponent): Instruction {
|
||||
var componentRecognizer = MapWrapper.get(this._rules, parentComponent);
|
||||
if (isBlank(componentRecognizer)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var componentSolutions = componentRecognizer.recognize(url);
|
||||
var fullSolutions = ListWrapper.create();
|
||||
var fullSolutions = [];
|
||||
|
||||
for(var i = 0; i < componentSolutions.length; i++) {
|
||||
var candidate = componentSolutions[i];
|
||||
|
@ -120,16 +119,14 @@ export class RouteRegistry {
|
|||
return null;
|
||||
}
|
||||
|
||||
generate(name:string, params:any, hostComponent) {
|
||||
generate(name:string, params:StringMap<string, string>, hostComponent): string {
|
||||
//TODO: implement for hierarchical routes
|
||||
var componentRecognizer = MapWrapper.get(this._rules, hostComponent);
|
||||
if (isPresent(componentRecognizer)) {
|
||||
return componentRecognizer.generate(name, params);
|
||||
}
|
||||
return isPresent(componentRecognizer) ? componentRecognizer.generate(name, params) : null;
|
||||
}
|
||||
}
|
||||
|
||||
function handlerToLeafInstructions(context, parentComponent) {
|
||||
function handlerToLeafInstructions(context, parentComponent): Instruction {
|
||||
var children = StringMapWrapper.create();
|
||||
StringMapWrapper.forEach(context['handler']['components'], (component, outletName) => {
|
||||
children[outletName] = new Instruction({
|
||||
|
@ -150,7 +147,7 @@ function handlerToLeafInstructions(context, parentComponent) {
|
|||
// { component: Foo }
|
||||
// mutates the config to:
|
||||
// { components: { default: Foo } }
|
||||
function normalizeConfig(config:StringMap) {
|
||||
function normalizeConfig(config:StringMap<string, any>): StringMap<string, any> {
|
||||
if (StringMapWrapper.contains(config, 'component')) {
|
||||
var component = StringMapWrapper.get(config, 'component');
|
||||
var components = StringMapWrapper.create();
|
||||
|
|
|
@ -51,18 +51,22 @@ export class Router {
|
|||
/**
|
||||
* Constructs a child router. You probably don't need to use this unless you're writing a reusable component.
|
||||
*/
|
||||
childRouter(outletName = 'default') {
|
||||
if (!MapWrapper.contains(this._children, outletName)) {
|
||||
MapWrapper.set(this._children, outletName, new ChildRouter(this, outletName));
|
||||
childRouter(outletName = 'default'): Router {
|
||||
var router = MapWrapper.get(this._children, outletName);
|
||||
|
||||
if (isBlank(router)) {
|
||||
router = new ChildRouter(this, outletName);
|
||||
MapWrapper.set(this._children, outletName, router);
|
||||
}
|
||||
return MapWrapper.get(this._children, outletName);
|
||||
|
||||
return router;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Register an object to notify of route changes. You probably don't need to use this unless you're writing a reusable component.
|
||||
*/
|
||||
registerOutlet(outlet:RouterOutlet, name = 'default'):Promise {
|
||||
registerOutlet(outlet:RouterOutlet, name: string = 'default'):Promise {
|
||||
MapWrapper.set(this._outlets, name, outlet);
|
||||
if (isPresent(this._currentInstruction)) {
|
||||
var childInstruction = this._currentInstruction.getChildInstruction(name);
|
||||
|
@ -91,12 +95,12 @@ export class Router {
|
|||
* ```
|
||||
*
|
||||
*/
|
||||
config(config:any) {
|
||||
config(config:any): Promise {
|
||||
if (config instanceof List) {
|
||||
config.forEach((configObject) => {
|
||||
// TODO: this is a hack
|
||||
this._registry.config(this.hostComponent, configObject);
|
||||
})
|
||||
});
|
||||
} else {
|
||||
this._registry.config(this.hostComponent, config);
|
||||
}
|
||||
|
@ -140,18 +144,18 @@ export class Router {
|
|||
return result;
|
||||
}
|
||||
|
||||
_startNavigating() {
|
||||
_startNavigating(): void {
|
||||
this.navigating = true;
|
||||
}
|
||||
|
||||
_finishNavigating() {
|
||||
_finishNavigating(): void {
|
||||
this.navigating = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to URL updates from the router
|
||||
*/
|
||||
subscribe(onNext) {
|
||||
subscribe(onNext): void {
|
||||
ObservableWrapper.subscribe(this._subject, onNext);
|
||||
}
|
||||
|
||||
|
@ -182,7 +186,7 @@ export class Router {
|
|||
/**
|
||||
* Given a URL, returns an instruction representing the component graph
|
||||
*/
|
||||
recognize(url:string) {
|
||||
recognize(url:string): Instruction {
|
||||
return this._registry.recognize(url, this.hostComponent);
|
||||
}
|
||||
|
||||
|
@ -202,7 +206,7 @@ export class Router {
|
|||
/**
|
||||
* Generate a URL from a component name and optional map of parameters. The URL is relative to the app's base href.
|
||||
*/
|
||||
generate(name:string, params:any) {
|
||||
generate(name:string, params:StringMap<string, string>): string {
|
||||
return this._registry.generate(name, params, this.hostComponent);
|
||||
}
|
||||
}
|
||||
|
@ -223,7 +227,7 @@ class ChildRouter extends Router {
|
|||
}
|
||||
}
|
||||
|
||||
function mapObjAsync(obj:Map, fn) {
|
||||
function mapObjAsync(obj:Map, fn): Promise {
|
||||
return PromiseWrapper.all(mapObj(obj, fn));
|
||||
}
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ import {Location} from './location';
|
|||
export class RouterLink {
|
||||
_domEl;
|
||||
_route:string;
|
||||
_params:any;
|
||||
_params:StringMap<string, string>;
|
||||
_router:Router;
|
||||
_location:Location;
|
||||
_href:string;
|
||||
|
@ -56,15 +56,15 @@ export class RouterLink {
|
|||
});
|
||||
}
|
||||
|
||||
set route(changes) {
|
||||
set route(changes: string) {
|
||||
this._route = changes;
|
||||
}
|
||||
|
||||
set params(changes) {
|
||||
set params(changes: StringMap) {
|
||||
this._params = changes;
|
||||
}
|
||||
|
||||
onAllChangesDone() {
|
||||
onAllChangesDone(): void {
|
||||
if (isPresent(this._route) && isPresent(this._params)) {
|
||||
var newHref = this._router.generate(this._route, this._params);
|
||||
this._href = this._location.normalizeAbsolutely(newHref);
|
||||
|
|
|
@ -29,7 +29,7 @@ export class RouterOutlet {
|
|||
this._router.registerOutlet(this, nameAttr);
|
||||
}
|
||||
|
||||
activate(instruction:Instruction) {
|
||||
activate(instruction:Instruction): Promise {
|
||||
return this._compiler.compileInHost(instruction.component).then((pv) => {
|
||||
var outletInjector = this._injector.resolveAndCreateChild([
|
||||
bind(RouteParams).toValue(new RouteParams(instruction.params)),
|
||||
|
@ -41,11 +41,11 @@ export class RouterOutlet {
|
|||
});
|
||||
}
|
||||
|
||||
canActivate(instruction:any) {
|
||||
canActivate(instruction:Instruction): Promise<boolean> {
|
||||
return PromiseWrapper.resolve(true);
|
||||
}
|
||||
|
||||
canDeactivate(instruction:any) {
|
||||
canDeactivate(instruction:Instruction): Promise<boolean> {
|
||||
return PromiseWrapper.resolve(true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ var specialCharacters = [
|
|||
|
||||
var escapeRe = RegExpWrapper.create('(\\' + specialCharacters.join('|\\') + ')', 'g');
|
||||
|
||||
export function escapeRegex(string:string) {
|
||||
export function escapeRegex(string:string): string {
|
||||
return StringWrapper.replaceAllMapped(string, escapeRe, (match) => {
|
||||
return "\\" + match;
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue