cleanup(router): fix tslint errors

This commit is contained in:
vsavkin 2016-06-08 11:13:41 -07:00
parent 8a1cdc2dd5
commit 6988a550ea
20 changed files with 439 additions and 371 deletions

View File

@ -1,10 +1,12 @@
import { RouterOutletMap } from './router_outlet_map'; import {Location, LocationStrategy, PathLocationStrategy} from '@angular/common';
import { UrlSerializer, DefaultUrlSerializer } from './url_serializer'; import {APP_INITIALIZER, ApplicationRef, ComponentResolver, Injector} from '@angular/core';
import { ActivatedRoute } from './router_state';
import { Router } from './router'; import {RouterConfig} from './config';
import { RouterConfig } from './config'; import {Router} from './router';
import { ComponentResolver, ApplicationRef, Injector, APP_INITIALIZER } from '@angular/core'; import {RouterOutletMap} from './router_outlet_map';
import { LocationStrategy, PathLocationStrategy, Location } from '@angular/common'; import {ActivatedRoute} from './router_state';
import {DefaultUrlSerializer, UrlSerializer} from './url_serializer';
/** /**
* A list of {@link Provider}s. To use the router, you must add this to your application. * A list of {@link Provider}s. To use the router, you must add this to your application.
@ -24,7 +26,7 @@ import { LocationStrategy, PathLocationStrategy, Location } from '@angular/commo
* bootstrap(AppCmp, [provideRouter(router)]); * bootstrap(AppCmp, [provideRouter(router)]);
* ``` * ```
*/ */
export function provideRouter(config: RouterConfig):any[] { export function provideRouter(config: RouterConfig): any[] {
return [ return [
Location, Location,
{provide: LocationStrategy, useClass: PathLocationStrategy}, {provide: LocationStrategy, useClass: PathLocationStrategy},
@ -34,20 +36,27 @@ export function provideRouter(config: RouterConfig):any[] {
provide: Router, provide: Router,
useFactory: (ref, resolver, urlSerializer, outletMap, location, injector) => { useFactory: (ref, resolver, urlSerializer, outletMap, location, injector) => {
if (ref.componentTypes.length == 0) { if (ref.componentTypes.length == 0) {
throw new Error("Bootstrap at least one component before injecting Router."); throw new Error('Bootstrap at least one component before injecting Router.');
} }
const componentType = ref.componentTypes[0]; const componentType = ref.componentTypes[0];
const r = new Router(componentType, resolver, urlSerializer, outletMap, location, injector, config); const r = new Router(
componentType, resolver, urlSerializer, outletMap, location, injector, config);
ref.registerDisposeListener(() => r.dispose()); ref.registerDisposeListener(() => r.dispose());
return r; return r;
}, },
deps: [ApplicationRef, ComponentResolver, UrlSerializer, RouterOutletMap, Location, Injector] deps:
[ApplicationRef, ComponentResolver, UrlSerializer, RouterOutletMap, Location, Injector]
}, },
RouterOutletMap, RouterOutletMap,
{provide: ActivatedRoute, useFactory: (r) => r.routerState.root, deps: [Router]}, {provide: ActivatedRoute, useFactory: (r) => r.routerState.root, deps: [Router]},
// Trigger initial navigation // Trigger initial navigation
{provide: APP_INITIALIZER, multi: true, useFactory: (router: Router) => router.initialNavigation(), deps: [Router]}, {
provide: APP_INITIALIZER,
multi: true,
useFactory: (router: Router) => router.initialNavigation(),
deps: [Router]
},
]; ];
} }

View File

@ -1,11 +1,11 @@
import { Type } from '@angular/core'; import {Type} from '@angular/core';
export type RouterConfig = Route[]; export type RouterConfig = Route[];
export interface Route { export interface Route {
index?: boolean; index?: boolean;
path?: string; path?: string;
component: Type | string; component: Type|string;
outlet?: string; outlet?: string;
canActivate?: any[]; canActivate?: any[];
canDeactivate?: any[]; canDeactivate?: any[];

View File

@ -1,6 +1,7 @@
import { RouterStateSnapshot, ActivatedRouteSnapshot, RouterState, ActivatedRoute } from './router_state'; import {BehaviorSubject} from 'rxjs/BehaviorSubject';
import { TreeNode } from './utils/tree';
import { BehaviorSubject } from 'rxjs/BehaviorSubject'; import {ActivatedRoute, ActivatedRouteSnapshot, RouterState, RouterStateSnapshot} from './router_state';
import {TreeNode} from './utils/tree';
export function createRouterState(curr: RouterStateSnapshot, prevState: RouterState): RouterState { export function createRouterState(curr: RouterStateSnapshot, prevState: RouterState): RouterState {
const root = createNode(curr._root, prevState ? prevState._root : undefined); const root = createNode(curr._root, prevState ? prevState._root : undefined);
@ -9,11 +10,12 @@ export function createRouterState(curr: RouterStateSnapshot, prevState: RouterSt
return new RouterState(root, queryParams, fragment, curr); return new RouterState(root, queryParams, fragment, curr);
} }
function createNode(curr:TreeNode<ActivatedRouteSnapshot>, prevState?:TreeNode<ActivatedRoute>):TreeNode<ActivatedRoute> { function createNode(curr: TreeNode<ActivatedRouteSnapshot>, prevState?: TreeNode<ActivatedRoute>):
TreeNode<ActivatedRoute> {
if (prevState && equalRouteSnapshots(prevState.value.snapshot, curr.value)) { if (prevState && equalRouteSnapshots(prevState.value.snapshot, curr.value)) {
const value = prevState.value; const value = prevState.value;
value._futureSnapshot = curr.value; value._futureSnapshot = curr.value;
const children = createOrReuseChildren(curr, prevState); const children = createOrReuseChildren(curr, prevState);
return new TreeNode<ActivatedRoute>(value, children); return new TreeNode<ActivatedRoute>(value, children);
@ -24,9 +26,11 @@ function createNode(curr:TreeNode<ActivatedRouteSnapshot>, prevState?:TreeNode<A
} }
} }
function createOrReuseChildren(curr:TreeNode<ActivatedRouteSnapshot>, prevState:TreeNode<ActivatedRoute>) { function createOrReuseChildren(
curr: TreeNode<ActivatedRouteSnapshot>, prevState: TreeNode<ActivatedRoute>) {
return curr.children.map(child => { return curr.children.map(child => {
const index = prevState.children.findIndex(p => equalRouteSnapshots(p.value.snapshot, child.value)); const index =
prevState.children.findIndex(p => equalRouteSnapshots(p.value.snapshot, child.value));
if (index >= 0) { if (index >= 0) {
return createNode(child, prevState.children[index]); return createNode(child, prevState.children[index]);
} else { } else {
@ -35,8 +39,9 @@ function createOrReuseChildren(curr:TreeNode<ActivatedRouteSnapshot>, prevState:
}); });
} }
function createActivatedRoute(c:ActivatedRouteSnapshot) { function createActivatedRoute(c: ActivatedRouteSnapshot) {
return new ActivatedRoute(new BehaviorSubject(c.urlSegments), new BehaviorSubject(c.params), c.outlet, c.component, c); return new ActivatedRoute(
new BehaviorSubject(c.urlSegments), new BehaviorSubject(c.params), c.outlet, c.component, c);
} }
function equalRouteSnapshots(a: ActivatedRouteSnapshot, b: ActivatedRouteSnapshot): boolean { function equalRouteSnapshots(a: ActivatedRouteSnapshot, b: ActivatedRouteSnapshot): boolean {

View File

@ -1,11 +1,12 @@
import { UrlTree, UrlSegment, equalUrlSegments } from './url_tree'; import {ActivatedRoute} from './router_state';
import { TreeNode } from './utils/tree'; import {PRIMARY_OUTLET, Params} from './shared';
import { forEach, shallowEqual } from './utils/collection'; import {UrlSegment, UrlTree} from './url_tree';
import { RouterState, ActivatedRoute } from './router_state'; import {forEach, shallowEqual} from './utils/collection';
import { Params, PRIMARY_OUTLET } from './shared'; import {TreeNode} from './utils/tree';
export function createUrlTree(route: ActivatedRoute, urlTree: UrlTree, commands: any[], export function createUrlTree(
queryParams: Params | undefined, fragment: string | undefined): UrlTree { route: ActivatedRoute, urlTree: UrlTree, commands: any[], queryParams: Params | undefined,
fragment: string | undefined): UrlTree {
if (commands.length === 0) { if (commands.length === 0) {
return tree(urlTree._root, urlTree, queryParams, fragment); return tree(urlTree._root, urlTree, queryParams, fragment);
} }
@ -24,7 +25,9 @@ export function createUrlTree(route: ActivatedRoute, urlTree: UrlTree, commands:
return tree(newRoot, urlTree, queryParams, fragment); return tree(newRoot, urlTree, queryParams, fragment);
} }
function tree(root: TreeNode<UrlSegment>, urlTree: UrlTree, queryParams: Params | undefined, fragment: string | undefined): UrlTree { function tree(
root: TreeNode<UrlSegment>, urlTree: UrlTree, queryParams: Params | undefined,
fragment: string | undefined): UrlTree {
const q = queryParams ? stringify(queryParams) : urlTree.queryParams; const q = queryParams ? stringify(queryParams) : urlTree.queryParams;
const f = fragment ? fragment : urlTree.fragment; const f = fragment ? fragment : urlTree.fragment;
return new UrlTree(root, q, f); return new UrlTree(root, q, f);
@ -32,16 +35,16 @@ function tree(root: TreeNode<UrlSegment>, urlTree: UrlTree, queryParams: Params
function navigateToRoot(normalizedChange: NormalizedNavigationCommands): boolean { function navigateToRoot(normalizedChange: NormalizedNavigationCommands): boolean {
return normalizedChange.isAbsolute && normalizedChange.commands.length === 1 && return normalizedChange.isAbsolute && normalizedChange.commands.length === 1 &&
normalizedChange.commands[0] == "/"; normalizedChange.commands[0] == '/';
} }
class NormalizedNavigationCommands { class NormalizedNavigationCommands {
constructor(public isAbsolute: boolean, public numberOfDoubleDots: number, constructor(
public commands: any[]) {} public isAbsolute: boolean, public numberOfDoubleDots: number, public commands: any[]) {}
} }
function normalizeCommands(commands: any[]): NormalizedNavigationCommands { function normalizeCommands(commands: any[]): NormalizedNavigationCommands {
if ((typeof commands[0] === "string") && commands.length === 1 && commands[0] == "/") { if ((typeof commands[0] === 'string') && commands.length === 1 && commands[0] == '/') {
return new NormalizedNavigationCommands(true, 0, commands); return new NormalizedNavigationCommands(true, 0, commands);
} }
@ -52,7 +55,7 @@ function normalizeCommands(commands: any[]): NormalizedNavigationCommands {
for (let i = 0; i < commands.length; ++i) { for (let i = 0; i < commands.length; ++i) {
const c = commands[i]; const c = commands[i];
if (!(typeof c === "string")) { if (!(typeof c === 'string')) {
res.push(c); res.push(c);
continue; continue;
} }
@ -63,11 +66,11 @@ function normalizeCommands(commands: any[]): NormalizedNavigationCommands {
// first exp is treated in a special way // first exp is treated in a special way
if (i == 0) { if (i == 0) {
if (j == 0 && cc == ".") { // './a' if (j == 0 && cc == '.') { // './a'
// skip it // skip it
} else if (j == 0 && cc == "") { // '/a' } else if (j == 0 && cc == '') { // '/a'
isAbsolute = true; isAbsolute = true;
} else if (cc == "..") { // '../a' } else if (cc == '..') { // '../a'
numberOfDoubleDots++; numberOfDoubleDots++;
} else if (cc != '') { } else if (cc != '') {
res.push(cc); res.push(cc);
@ -84,22 +87,23 @@ function normalizeCommands(commands: any[]): NormalizedNavigationCommands {
return new NormalizedNavigationCommands(isAbsolute, numberOfDoubleDots, res); return new NormalizedNavigationCommands(isAbsolute, numberOfDoubleDots, res);
} }
function findStartingNode(normalizedChange: NormalizedNavigationCommands, urlTree: UrlTree, function findStartingNode(
route: ActivatedRoute): TreeNode<UrlSegment> { normalizedChange: NormalizedNavigationCommands, urlTree: UrlTree,
route: ActivatedRoute): TreeNode<UrlSegment> {
if (normalizedChange.isAbsolute) { if (normalizedChange.isAbsolute) {
return urlTree._root; return urlTree._root;
} else { } else {
const urlSegment = const urlSegment = findUrlSegment(route, urlTree, normalizedChange.numberOfDoubleDots);
findUrlSegment(route, urlTree, normalizedChange.numberOfDoubleDots);
return findMatchingNode(urlSegment, urlTree._root); return findMatchingNode(urlSegment, urlTree._root);
} }
} }
function findUrlSegment(route: ActivatedRoute, urlTree: UrlTree, numberOfDoubleDots: number): UrlSegment { function findUrlSegment(
route: ActivatedRoute, urlTree: UrlTree, numberOfDoubleDots: number): UrlSegment {
const urlSegment = route.snapshot._lastUrlSegment; const urlSegment = route.snapshot._lastUrlSegment;
const path = urlTree.pathFromRoot(urlSegment); const path = urlTree.pathFromRoot(urlSegment);
if (path.length <= numberOfDoubleDots) { if (path.length <= numberOfDoubleDots) {
throw new Error("Invalid number of '../'"); throw new Error('Invalid number of \'../\'');
} }
return path[path.length - 1 - numberOfDoubleDots]; return path[path.length - 1 - numberOfDoubleDots];
} }
@ -113,13 +117,14 @@ function findMatchingNode(segment: UrlSegment, node: TreeNode<UrlSegment>): Tree
throw new Error(`Cannot find url segment '${segment}'`); throw new Error(`Cannot find url segment '${segment}'`);
} }
function constructNewTree(node: TreeNode<UrlSegment>, original: TreeNode<UrlSegment>, function constructNewTree(
updated: TreeNode<UrlSegment>[]): TreeNode<UrlSegment> { node: TreeNode<UrlSegment>, original: TreeNode<UrlSegment>,
updated: TreeNode<UrlSegment>[]): TreeNode<UrlSegment> {
if (node === original) { if (node === original) {
return new TreeNode<UrlSegment>(node.value, updated); return new TreeNode<UrlSegment>(node.value, updated);
} else { } else {
return new TreeNode<UrlSegment>( return new TreeNode<UrlSegment>(
node.value, node.children.map(c => constructNewTree(c, original, updated))); node.value, node.children.map(c => constructNewTree(c, original, updated)));
} }
} }
@ -136,18 +141,18 @@ function updateMany(nodes: TreeNode<UrlSegment>[], commands: any[]): TreeNode<Ur
} }
function getPath(commands: any[]): any { function getPath(commands: any[]): any {
if (!(typeof commands[0] === "string")) return commands[0]; if (!(typeof commands[0] === 'string')) return commands[0];
const parts = commands[0].toString().split(":"); const parts = commands[0].toString().split(':');
return parts.length > 1 ? parts[1] : commands[0]; return parts.length > 1 ? parts[1] : commands[0];
} }
function getOutlet(commands: any[]): string { function getOutlet(commands: any[]): string {
if (!(typeof commands[0] === "string")) return PRIMARY_OUTLET; if (!(typeof commands[0] === 'string')) return PRIMARY_OUTLET;
const parts = commands[0].toString().split(":"); const parts = commands[0].toString().split(':');
return parts.length > 1 ? parts[0] : PRIMARY_OUTLET; return parts.length > 1 ? parts[0] : PRIMARY_OUTLET;
} }
function update(node: TreeNode<UrlSegment>|null, commands: any[]): TreeNode<UrlSegment> { function update(node: TreeNode<UrlSegment>| null, commands: any[]): TreeNode<UrlSegment> {
const rest = commands.slice(1); const rest = commands.slice(1);
const next = rest.length === 0 ? null : rest[0]; const next = rest.length === 0 ? null : rest[0];
const outlet = getOutlet(commands); const outlet = getOutlet(commands);
@ -202,8 +207,8 @@ function compare(path: string, params: {[key: string]: any}, segment: UrlSegment
return path == segment.path && shallowEqual(params, segment.parameters); return path == segment.path && shallowEqual(params, segment.parameters);
} }
function recurse(urlSegment: UrlSegment, node: TreeNode<UrlSegment> | null, function recurse(
rest: any[]): TreeNode<UrlSegment> { urlSegment: UrlSegment, node: TreeNode<UrlSegment>| null, rest: any[]): TreeNode<UrlSegment> {
if (rest.length === 0) { if (rest.length === 0) {
return new TreeNode<UrlSegment>(urlSegment, []); return new TreeNode<UrlSegment>(urlSegment, []);
} }

View File

@ -1,13 +1,9 @@
import { import {Directive, HostBinding, HostListener, Input, OnChanges} from '@angular/core';
Directive,
HostListener,
HostBinding,
Input,
OnChanges
} from '@angular/core';
import {Router} from '../router'; import {Router} from '../router';
import {ActivatedRoute} from '../router_state'; import {ActivatedRoute} from '../router_state';
/** /**
* The RouterLink directive lets you link to specific parts of your app. * The RouterLink directive lets you link to specific parts of your app.
* *
@ -37,7 +33,7 @@ import {ActivatedRoute} from '../router_state';
export class RouterLink implements OnChanges { export class RouterLink implements OnChanges {
@Input() target: string; @Input() target: string;
private commands: any[] = []; private commands: any[] = [];
@Input() queryParams: {[k:string]:any}; @Input() queryParams: {[k: string]: any};
@Input() fragment: string; @Input() fragment: string;
// the url displayed on the anchor element. // the url displayed on the anchor element.
@ -49,7 +45,7 @@ export class RouterLink implements OnChanges {
constructor(private router: Router, private route: ActivatedRoute) {} constructor(private router: Router, private route: ActivatedRoute) {}
@Input() @Input()
set routerLink(data: any[] | string) { set routerLink(data: any[]|string) {
if (Array.isArray(data)) { if (Array.isArray(data)) {
this.commands = <any>data; this.commands = <any>data;
} else { } else {
@ -57,30 +53,24 @@ export class RouterLink implements OnChanges {
} }
} }
ngOnChanges(changes:{}):any { ngOnChanges(changes: {}): any { this.updateTargetUrlAndHref(); }
this.updateTargetUrlAndHref();
}
@HostListener("click") @HostListener('click')
onClick(): boolean { onClick(): boolean {
// If no target, or if target is _self, prevent default browser behavior // If no target, or if target is _self, prevent default browser behavior
if (!(typeof this.target === "string") || this.target == '_self') { if (!(typeof this.target === 'string') || this.target == '_self') {
this.router.navigate(this.commands, { this.router.navigate(
relativeTo: this.route, this.commands,
queryParams: this.queryParams, {relativeTo: this.route, queryParams: this.queryParams, fragment: this.fragment});
fragment: this.fragment
});
return false; return false;
} }
return true; return true;
} }
private updateTargetUrlAndHref(): void { private updateTargetUrlAndHref(): void {
const tree = this.router.createUrlTree(this.commands, { const tree = this.router.createUrlTree(
relativeTo: this.route, this.commands,
queryParams: this.queryParams, {relativeTo: this.route, queryParams: this.queryParams, fragment: this.fragment});
fragment: this.fragment
});
if (tree) { if (tree) {
this.href = this.router.serializeUrl(tree); this.href = this.router.serializeUrl(tree);
} }

View File

@ -1,23 +1,24 @@
import {Injector, Directive, ViewContainerRef, Attribute, ComponentRef, ComponentFactory, ResolvedReflectiveProvider, ReflectiveInjector} from '@angular/core'; import {Attribute, ComponentFactory, ComponentRef, Directive, ReflectiveInjector, ResolvedReflectiveProvider, ViewContainerRef} from "@angular/core";
import {RouterOutletMap} from '../router_outlet_map'; import {RouterOutletMap} from "../router_outlet_map";
import {PRIMARY_OUTLET} from '../shared'; import {PRIMARY_OUTLET} from "../shared";
@Directive({selector: 'router-outlet'}) @Directive({selector: 'router-outlet'})
export class RouterOutlet { export class RouterOutlet {
private activated:ComponentRef<any>|null; private activated: ComponentRef<any>|null;
public outletMap:RouterOutletMap; public outletMap: RouterOutletMap;
/** /**
* @internal * @internal
*/ */
constructor(parentOutletMap:RouterOutletMap, private location:ViewContainerRef, constructor(
@Attribute('name') name:string) { parentOutletMap: RouterOutletMap, private location: ViewContainerRef,
@Attribute('name') name: string) {
parentOutletMap.registerOutlet(name ? name : PRIMARY_OUTLET, this); parentOutletMap.registerOutlet(name ? name : PRIMARY_OUTLET, this);
} }
get isActivated(): boolean { return !!this.activated; } get isActivated(): boolean { return !!this.activated; }
get component(): Object { get component(): Object {
if (!this.activated) throw new Error("Outlet is not activated"); if (!this.activated) throw new Error('Outlet is not activated');
return this.activated.instance; return this.activated.instance;
} }
@ -28,8 +29,9 @@ export class RouterOutlet {
} }
} }
activate(factory: ComponentFactory<any>, providers: ResolvedReflectiveProvider[], activate(
outletMap: RouterOutletMap): void { factory: ComponentFactory<any>, providers: ResolvedReflectiveProvider[],
outletMap: RouterOutletMap): void {
this.outletMap = outletMap; this.outletMap = outletMap;
const inj = ReflectiveInjector.fromResolvedProviders(providers, this.location.parentInjector); const inj = ReflectiveInjector.fromResolvedProviders(providers, this.location.parentInjector);
this.activated = this.location.createComponent(factory, this.location.length, inj, []); this.activated = this.location.createComponent(factory, this.location.length, inj, []);

View File

@ -1,14 +1,14 @@
export { Router, Event, NavigationStart, NavigationEnd, NavigationCancel, NavigationError } from './router'; import {RouterLink} from './directives/router_link';
export { UrlSerializer, DefaultUrlSerializer } from './url_serializer'; import {RouterOutlet} from './directives/router_outlet';
export { RouterState, ActivatedRoute, RouterStateSnapshot, ActivatedRouteSnapshot } from './router_state';
export { UrlTree, UrlSegment} from './url_tree';
export { RouterOutletMap } from './router_outlet_map';
export { RouterConfig, Route } from './config';
export { Params, PRIMARY_OUTLET } from './shared';
export { provideRouter } from './router_providers';
export { CanActivate, CanDeactivate } from './interfaces';
import { RouterOutlet } from './directives/router_outlet'; export {Route, RouterConfig} from './config';
import { RouterLink } from './directives/router_link'; export {CanActivate, CanDeactivate} from './interfaces';
export {Event, NavigationCancel, NavigationEnd, NavigationError, NavigationStart, Router} from './router';
export {RouterOutletMap} from './router_outlet_map';
export {provideRouter} from './router_providers';
export {ActivatedRoute, ActivatedRouteSnapshot, RouterState, RouterStateSnapshot} from './router_state';
export {PRIMARY_OUTLET, Params} from './shared';
export {DefaultUrlSerializer, UrlSerializer} from './url_serializer';
export {UrlSegment, UrlTree} from './url_tree';
export const ROUTER_DIRECTIVES = [RouterOutlet, RouterLink]; export const ROUTER_DIRECTIVES = [RouterOutlet, RouterLink];

View File

@ -5,12 +5,14 @@ import {ActivatedRouteSnapshot, RouterStateSnapshot} from './router_state';
* An interface a class can implement to be a guard deciding if a route can be activated. * An interface a class can implement to be a guard deciding if a route can be activated.
*/ */
export interface CanActivate { export interface CanActivate {
canActivate(route:ActivatedRouteSnapshot, state:RouterStateSnapshot):Observable<boolean> | boolean; canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot):
Observable<boolean>|boolean;
} }
/** /**
* An interface a class can implement to be a guard deciding if a route can be deactivated. * An interface a class can implement to be a guard deciding if a route can be deactivated.
*/ */
export interface CanDeactivate<T> { export interface CanDeactivate<T> {
canDeactivate(component:T, route:ActivatedRouteSnapshot, state:RouterStateSnapshot):Observable<boolean> | boolean; canDeactivate(component: T, route: ActivatedRouteSnapshot, state: RouterStateSnapshot):
Observable<boolean>|boolean;
} }

View File

@ -1,26 +1,31 @@
import { UrlTree, UrlSegment } from './url_tree'; import {Type} from '@angular/core';
import { flatten, first, merge } from './utils/collection'; import {Observable} from 'rxjs/Observable';
import { TreeNode } from './utils/tree';
import { RouterStateSnapshot, ActivatedRouteSnapshot } from './router_state'; import {Route, RouterConfig} from './config';
import { PRIMARY_OUTLET } from './shared'; import {ActivatedRouteSnapshot, RouterStateSnapshot} from './router_state';
import { RouterConfig, Route } from './config'; import {PRIMARY_OUTLET} from './shared';
import { Type } from '@angular/core'; import {UrlSegment, UrlTree} from './url_tree';
import { Observable } from 'rxjs/Observable'; import {first, flatten, merge} from './utils/collection';
import {TreeNode} from './utils/tree';
class CannotRecognize {} class CannotRecognize {}
export function recognize(rootComponentType: Type, config: RouterConfig, url: UrlTree): Observable<RouterStateSnapshot> { export function recognize(
rootComponentType: Type, config: RouterConfig, url: UrlTree): Observable<RouterStateSnapshot> {
try { try {
const match = new MatchResult(rootComponentType, config, [url.root], {}, url._root.children, [], PRIMARY_OUTLET, null, url.root); const match = new MatchResult(
rootComponentType, config, [url.root], {}, url._root.children, [], PRIMARY_OUTLET, null,
url.root);
const roots = constructActivatedRoute(match); const roots = constructActivatedRoute(match);
const res = new RouterStateSnapshot(roots[0], url.queryParams, url.fragment); const res = new RouterStateSnapshot(roots[0], url.queryParams, url.fragment);
return new Observable<RouterStateSnapshot>(obs => { return new Observable<RouterStateSnapshot>(obs => {
obs.next(res); obs.next(res);
obs.complete(); obs.complete();
}); });
} catch(e) { } catch (e) {
if (e instanceof CannotRecognize) { if (e instanceof CannotRecognize) {
return new Observable<RouterStateSnapshot>(obs => obs.error(new Error("Cannot match any routes"))); return new Observable<RouterStateSnapshot>(
obs => obs.error(new Error('Cannot match any routes')));
} else { } else {
return new Observable<RouterStateSnapshot>(obs => obs.error(e)); return new Observable<RouterStateSnapshot>(obs => obs.error(e));
} }
@ -30,7 +35,8 @@ export function recognize(rootComponentType: Type, config: RouterConfig, url: Ur
function constructActivatedRoute(match: MatchResult): TreeNode<ActivatedRouteSnapshot>[] { function constructActivatedRoute(match: MatchResult): TreeNode<ActivatedRouteSnapshot>[] {
const activatedRoute = createActivatedRouteSnapshot(match); const activatedRoute = createActivatedRouteSnapshot(match);
const children = match.leftOverUrl.length > 0 ? const children = match.leftOverUrl.length > 0 ?
recognizeMany(match.children, match.leftOverUrl) : recognizeLeftOvers(match.children, match.lastUrlSegment); recognizeMany(match.children, match.leftOverUrl) :
recognizeLeftOvers(match.children, match.lastUrlSegment);
checkOutletNameUniqueness(children); checkOutletNameUniqueness(children);
children.sort((a, b) => { children.sort((a, b) => {
if (a.value.outlet === PRIMARY_OUTLET) return -1; if (a.value.outlet === PRIMARY_OUTLET) return -1;
@ -40,23 +46,28 @@ function constructActivatedRoute(match: MatchResult): TreeNode<ActivatedRouteSna
return [new TreeNode<ActivatedRouteSnapshot>(activatedRoute, children)]; return [new TreeNode<ActivatedRouteSnapshot>(activatedRoute, children)];
} }
function recognizeLeftOvers(config: Route[], lastUrlSegment: UrlSegment): TreeNode<ActivatedRouteSnapshot>[] { function recognizeLeftOvers(
config: Route[], lastUrlSegment: UrlSegment): TreeNode<ActivatedRouteSnapshot>[] {
if (!config) return []; if (!config) return [];
const mIndex = matchIndex(config, [], lastUrlSegment); const mIndex = matchIndex(config, [], lastUrlSegment);
return mIndex ? constructActivatedRoute(mIndex) : []; return mIndex ? constructActivatedRoute(mIndex) : [];
} }
function recognizeMany(config: Route[], urls: TreeNode<UrlSegment>[]): TreeNode<ActivatedRouteSnapshot>[] { function recognizeMany(
config: Route[], urls: TreeNode<UrlSegment>[]): TreeNode<ActivatedRouteSnapshot>[] {
return flatten(urls.map(url => recognizeOne(config, url))); return flatten(urls.map(url => recognizeOne(config, url)));
} }
function createActivatedRouteSnapshot(match: MatchResult): ActivatedRouteSnapshot { function createActivatedRouteSnapshot(match: MatchResult): ActivatedRouteSnapshot {
return new ActivatedRouteSnapshot(match.consumedUrlSegments, match.parameters, match.outlet, match.component, match.route, match.lastUrlSegment); return new ActivatedRouteSnapshot(
match.consumedUrlSegments, match.parameters, match.outlet, match.component, match.route,
match.lastUrlSegment);
} }
function recognizeOne(config: Route[], url: TreeNode<UrlSegment>): TreeNode<ActivatedRouteSnapshot>[] { function recognizeOne(
config: Route[], url: TreeNode<UrlSegment>): TreeNode<ActivatedRouteSnapshot>[] {
const matches = match(config, url); const matches = match(config, url);
for(let match of matches) { for (let match of matches) {
try { try {
const primary = constructActivatedRoute(match); const primary = constructActivatedRoute(match);
const secondary = recognizeMany(config, match.secondary); const secondary = recognizeMany(config, match.secondary);
@ -64,7 +75,7 @@ function recognizeOne(config: Route[], url: TreeNode<UrlSegment>): TreeNode<Acti
checkOutletNameUniqueness(res); checkOutletNameUniqueness(res);
return res; return res;
} catch (e) { } catch (e) {
if (! (e instanceof CannotRecognize)) { if (!(e instanceof CannotRecognize)) {
throw e; throw e;
} }
} }
@ -72,13 +83,14 @@ function recognizeOne(config: Route[], url: TreeNode<UrlSegment>): TreeNode<Acti
throw new CannotRecognize(); throw new CannotRecognize();
} }
function checkOutletNameUniqueness(nodes: TreeNode<ActivatedRouteSnapshot>[]): TreeNode<ActivatedRouteSnapshot>[] { function checkOutletNameUniqueness(nodes: TreeNode<ActivatedRouteSnapshot>[]):
TreeNode<ActivatedRouteSnapshot>[] {
let names = {}; let names = {};
nodes.forEach(n => { nodes.forEach(n => {
let routeWithSameOutletName = names[n.value.outlet]; let routeWithSameOutletName = names[n.value.outlet];
if (routeWithSameOutletName) { if (routeWithSameOutletName) {
const p = routeWithSameOutletName.urlSegments.map(s => s.toString()).join("/"); const p = routeWithSameOutletName.urlSegments.map(s => s.toString()).join('/');
const c = n.value.urlSegments.map(s => s.toString()).join("/"); const c = n.value.urlSegments.map(s => s.toString()).join('/');
throw new Error(`Two segments cannot have the same outlet name: '${p}' and '${c}'.`); throw new Error(`Two segments cannot have the same outlet name: '${p}' and '${c}'.`);
} }
names[n.value.outlet] = n.value; names[n.value.outlet] = n.value;
@ -99,13 +111,18 @@ function match(config: Route[], url: TreeNode<UrlSegment>): MatchResult[] {
return res; return res;
} }
function createIndexMatch(r: Route, leftOverUrls:TreeNode<UrlSegment>[], lastUrlSegment:UrlSegment): MatchResult { function createIndexMatch(
r: Route, leftOverUrls: TreeNode<UrlSegment>[], lastUrlSegment: UrlSegment): MatchResult {
const outlet = r.outlet ? r.outlet : PRIMARY_OUTLET; const outlet = r.outlet ? r.outlet : PRIMARY_OUTLET;
const children = r.children ? r.children : []; const children = r.children ? r.children : [];
return new MatchResult(r.component, children, [], lastUrlSegment.parameters, leftOverUrls, [], outlet, r, lastUrlSegment); return new MatchResult(
r.component, children, [], lastUrlSegment.parameters, leftOverUrls, [], outlet, r,
lastUrlSegment);
} }
function matchIndex(config: Route[], leftOverUrls: TreeNode<UrlSegment>[], lastUrlSegment: UrlSegment): MatchResult | null { function matchIndex(
config: Route[], leftOverUrls: TreeNode<UrlSegment>[], lastUrlSegment: UrlSegment): MatchResult|
null {
for (let r of config) { for (let r of config) {
if (r.index) { if (r.index) {
return createIndexMatch(r, leftOverUrls, lastUrlSegment); return createIndexMatch(r, leftOverUrls, lastUrlSegment);
@ -114,23 +131,24 @@ function matchIndex(config: Route[], leftOverUrls: TreeNode<UrlSegment>[], lastU
return null; return null;
} }
function matchWithParts(route: Route, url: TreeNode<UrlSegment>): MatchResult | null { function matchWithParts(route: Route, url: TreeNode<UrlSegment>): MatchResult|null {
if (!route.path) return null; if (!route.path) return null;
if ((route.outlet ? route.outlet : PRIMARY_OUTLET) !== url.value.outlet) return null; if ((route.outlet ? route.outlet : PRIMARY_OUTLET) !== url.value.outlet) return null;
const path = route.path.startsWith("/") ? route.path.substring(1) : route.path; const path = route.path.startsWith('/') ? route.path.substring(1) : route.path;
if (path === "**") { if (path === '**') {
const consumedUrl = []; const consumedUrl = [];
let u:TreeNode<UrlSegment>|null = url; let u: TreeNode<UrlSegment>|null = url;
while (u) { while (u) {
consumedUrl.push(u.value); consumedUrl.push(u.value);
u = first(u.children); u = first(u.children);
} }
const last = consumedUrl[consumedUrl.length - 1]; const last = consumedUrl[consumedUrl.length - 1];
return new MatchResult(route.component, [], consumedUrl, last.parameters, [], [], PRIMARY_OUTLET, route, last); return new MatchResult(
route.component, [], consumedUrl, last.parameters, [], [], PRIMARY_OUTLET, route, last);
} }
const parts = path.split("/"); const parts = path.split('/');
const positionalParams = {}; const positionalParams = {};
const consumedUrlSegments = []; const consumedUrlSegments = [];
@ -144,7 +162,7 @@ function matchWithParts(route: Route, url: TreeNode<UrlSegment>): MatchResult |
const p = parts[i]; const p = parts[i];
const isLastSegment = i === parts.length - 1; const isLastSegment = i === parts.length - 1;
const isLastParent = i === parts.length - 2; const isLastParent = i === parts.length - 2;
const isPosParam = p.startsWith(":"); const isPosParam = p.startsWith(':');
if (!isPosParam && p != current.value.path) return null; if (!isPosParam && p != current.value.path) return null;
if (isLastSegment) { if (isLastSegment) {
@ -163,7 +181,7 @@ function matchWithParts(route: Route, url: TreeNode<UrlSegment>): MatchResult |
current = first(current.children); current = first(current.children);
} }
if (!lastSegment) throw "Cannot be reached"; if (!lastSegment) throw 'Cannot be reached';
const p = lastSegment.value.parameters; const p = lastSegment.value.parameters;
const parameters = <{[key: string]: string}>merge(p, positionalParams); const parameters = <{[key: string]: string}>merge(p, positionalParams);
@ -171,19 +189,15 @@ function matchWithParts(route: Route, url: TreeNode<UrlSegment>): MatchResult |
const children = route.children ? route.children : []; const children = route.children ? route.children : [];
const outlet = route.outlet ? route.outlet : PRIMARY_OUTLET; const outlet = route.outlet ? route.outlet : PRIMARY_OUTLET;
return new MatchResult(route.component, children, consumedUrlSegments, parameters, lastSegment.children, return new MatchResult(
secondarySubtrees, outlet, route, lastSegment.value); route.component, children, consumedUrlSegments, parameters, lastSegment.children,
secondarySubtrees, outlet, route, lastSegment.value);
} }
class MatchResult { class MatchResult {
constructor(public component: Type | string, constructor(
public children: Route[], public component: Type|string, public children: Route[],
public consumedUrlSegments: UrlSegment[], public consumedUrlSegments: UrlSegment[], public parameters: {[key: string]: string},
public parameters: {[key: string]: string}, public leftOverUrl: TreeNode<UrlSegment>[], public secondary: TreeNode<UrlSegment>[],
public leftOverUrl: TreeNode<UrlSegment>[], public outlet: string, public route: Route|null, public lastUrlSegment: UrlSegment) {}
public secondary: TreeNode<UrlSegment>[],
public outlet: string,
public route: Route | null,
public lastUrlSegment: UrlSegment
) {}
} }

View File

@ -1,28 +1,33 @@
import { RouterStateSnapshot, ActivatedRouteSnapshot } from './router_state';
import { TreeNode } from './utils/tree';
import { ComponentResolver } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map'; import 'rxjs/add/operator/map';
import {forkJoin} from 'rxjs/observable/forkJoin';
import {fromPromise} from 'rxjs/observable/fromPromise';
import 'rxjs/add/operator/toPromise'; import 'rxjs/add/operator/toPromise';
export function resolve(resolver: ComponentResolver, state: RouterStateSnapshot): Observable<RouterStateSnapshot> { import {ComponentResolver} from '@angular/core';
import {Observable} from 'rxjs/Observable';
import {forkJoin} from 'rxjs/observable/forkJoin';
import {fromPromise} from 'rxjs/observable/fromPromise';
import {ActivatedRouteSnapshot, RouterStateSnapshot} from './router_state';
import {TreeNode} from './utils/tree';
export function resolve(
resolver: ComponentResolver, state: RouterStateSnapshot): Observable<RouterStateSnapshot> {
return resolveNode(resolver, state._root).map(_ => state); return resolveNode(resolver, state._root).map(_ => state);
} }
function resolveNode(resolver: ComponentResolver, node: TreeNode<ActivatedRouteSnapshot>): Observable<any> { function resolveNode(
resolver: ComponentResolver, node: TreeNode<ActivatedRouteSnapshot>): Observable<any> {
if (node.children.length === 0) { if (node.children.length === 0) {
return fromPromise(resolver.resolveComponent(<any>node.value.component).then(factory => { return fromPromise(resolver.resolveComponent(<any>node.value.component).then(factory => {
node.value._resolvedComponentFactory = factory; node.value._resolvedComponentFactory = factory;
return node.value; return node.value;
})); }));
} else { } else {
const c = node.children.map(c => resolveNode(resolver, c).toPromise()); const c = node.children.map(c => resolveNode(resolver, c).toPromise());
return forkJoin(c).map(_ => resolver.resolveComponent(<any>node.value.component).then(factory => { return forkJoin(c).map(
node.value._resolvedComponentFactory = factory; _ => resolver.resolveComponent(<any>node.value.component).then(factory => {
return node.value; node.value._resolvedComponentFactory = factory;
})); return node.value;
}));
} }
} }

View File

@ -1,50 +1,64 @@
import { ComponentResolver, ReflectiveInjector, Type, Injector } from '@angular/core';
import { Location } from '@angular/common';
import { UrlSerializer } from './url_serializer';
import { RouterOutletMap } from './router_outlet_map';
import { recognize } from './recognize';
import { resolve } from './resolve';
import { createRouterState } from './create_router_state';
import { TreeNode } from './utils/tree';
import { UrlTree, createEmptyUrlTree } from './url_tree';
import { PRIMARY_OUTLET, Params } from './shared';
import { createEmptyState, RouterState, RouterStateSnapshot, ActivatedRoute, ActivatedRouteSnapshot, advanceActivatedRoute} from './router_state';
import { RouterConfig } from './config';
import { RouterOutlet } from './directives/router_outlet';
import { createUrlTree } from './create_url_tree';
import { forEach, and, shallowEqual } from './utils/collection';
import { Observable } from 'rxjs/Observable';
import { Subscription } from 'rxjs/Subscription';
import { Subject } from 'rxjs/Subject';
import 'rxjs/add/operator/map'; import 'rxjs/add/operator/map';
import 'rxjs/add/operator/scan'; import 'rxjs/add/operator/scan';
import 'rxjs/add/operator/mergeMap'; import 'rxjs/add/operator/mergeMap';
import 'rxjs/add/operator/concat'; import 'rxjs/add/operator/concat';
import 'rxjs/add/operator/concatMap'; import 'rxjs/add/operator/concatMap';
import {of} from 'rxjs/observable/of';
import {forkJoin} from 'rxjs/observable/forkJoin';
export interface NavigationExtras { relativeTo?: ActivatedRoute; queryParams?: Params; fragment?: string; } import {Location} from '@angular/common';
import {ComponentResolver, Injector, ReflectiveInjector, Type} from '@angular/core';
import {Observable} from 'rxjs/Observable';
import {Subject} from 'rxjs/Subject';
import {Subscription} from 'rxjs/Subscription';
import {forkJoin} from 'rxjs/observable/forkJoin';
import {of } from 'rxjs/observable/of';
import {RouterConfig} from './config';
import {createRouterState} from './create_router_state';
import {createUrlTree} from './create_url_tree';
import {RouterOutlet} from './directives/router_outlet';
import {recognize} from './recognize';
import {resolve} from './resolve';
import {RouterOutletMap} from './router_outlet_map';
import {ActivatedRoute, ActivatedRouteSnapshot, RouterState, RouterStateSnapshot, advanceActivatedRoute, createEmptyState} from './router_state';
import {PRIMARY_OUTLET, Params} from './shared';
import {UrlSerializer} from './url_serializer';
import {UrlTree, createEmptyUrlTree} from './url_tree';
import {and, forEach, shallowEqual} from './utils/collection';
import {TreeNode} from './utils/tree';
export interface NavigationExtras {
relativeTo?: ActivatedRoute;
queryParams?: Params;
fragment?: string;
}
/** /**
* An event triggered when a navigation starts * An event triggered when a navigation starts
*/ */
export class NavigationStart { constructor(public id:number, public url:UrlTree) {} } export class NavigationStart {
constructor(public id: number, public url: UrlTree) {}
}
/** /**
* An event triggered when a navigation ends successfully * An event triggered when a navigation ends successfully
*/ */
export class NavigationEnd { constructor(public id:number, public url:UrlTree) {} } export class NavigationEnd {
constructor(public id: number, public url: UrlTree) {}
}
/** /**
* An event triggered when a navigation is canceled * An event triggered when a navigation is canceled
*/ */
export class NavigationCancel { constructor(public id:number, public url:UrlTree) {} } export class NavigationCancel {
constructor(public id: number, public url: UrlTree) {}
}
/** /**
* An event triggered when a navigation fails due to unexpected error * An event triggered when a navigation fails due to unexpected error
*/ */
export class NavigationError { constructor(public id:number, public url:UrlTree, public error:any) {} } export class NavigationError {
constructor(public id: number, public url: UrlTree, public error: any) {}
}
export type Event = NavigationStart | NavigationEnd | NavigationCancel | NavigationError; export type Event = NavigationStart | NavigationEnd | NavigationCancel | NavigationError;
@ -61,7 +75,10 @@ export class Router {
/** /**
* @internal * @internal
*/ */
constructor(private rootComponentType:Type, private resolver: ComponentResolver, private urlSerializer: UrlSerializer, private outletMap: RouterOutletMap, private location: Location, private injector: Injector, private config: RouterConfig) { constructor(
private rootComponentType: Type, private resolver: ComponentResolver,
private urlSerializer: UrlSerializer, private outletMap: RouterOutletMap,
private location: Location, private injector: Injector, private config: RouterConfig) {
this.routerEvents = new Subject<Event>(); this.routerEvents = new Subject<Event>();
this.currentUrlTree = createEmptyUrlTree(); this.currentUrlTree = createEmptyUrlTree();
this.currentRouterState = createEmptyState(this.rootComponentType); this.currentRouterState = createEmptyState(this.rootComponentType);
@ -70,7 +87,7 @@ export class Router {
/** /**
* @internal * @internal
*/ */
initialNavigation():void { initialNavigation(): void {
this.setUpLocationChangeListener(); this.setUpLocationChangeListener();
this.navigateByUrl(this.location.path()); this.navigateByUrl(this.location.path());
} }
@ -78,23 +95,17 @@ export class Router {
/** /**
* Returns the current route state. * Returns the current route state.
*/ */
get routerState(): RouterState { get routerState(): RouterState { return this.currentRouterState; }
return this.currentRouterState;
}
/** /**
* Returns the current url tree. * Returns the current url tree.
*/ */
get urlTree(): UrlTree { get urlTree(): UrlTree { return this.currentUrlTree; }
return this.currentUrlTree;
}
/** /**
* Returns an observable of route events * Returns an observable of route events
*/ */
get events(): Observable<Event> { get events(): Observable<Event> { return this.routerEvents; }
return this.routerEvents;
}
/** /**
* Navigate based on the provided url. This navigation is always absolute. * Navigate based on the provided url. This navigation is always absolute.
@ -129,9 +140,7 @@ export class Router {
* ]); * ]);
* ``` * ```
*/ */
resetConfig(config: RouterConfig): void { resetConfig(config: RouterConfig): void { this.config = config; }
this.config = config;
}
/** /**
* @internal * @internal
@ -169,7 +178,8 @@ export class Router {
* router.createUrlTree(['../../team/44/user/22'], {relativeTo: route}); * router.createUrlTree(['../../team/44/user/22'], {relativeTo: route});
* ``` * ```
*/ */
createUrlTree(commands: any[], {relativeTo, queryParams, fragment}: NavigationExtras = {}): UrlTree { createUrlTree(commands: any[], {relativeTo, queryParams, fragment}: NavigationExtras = {}):
UrlTree {
const a = relativeTo ? relativeTo : this.routerState.root; const a = relativeTo ? relativeTo : this.routerState.root;
return createUrlTree(a, this.currentUrlTree, commands, queryParams, fragment); return createUrlTree(a, this.currentUrlTree, commands, queryParams, fragment);
} }
@ -204,8 +214,8 @@ export class Router {
*/ */
parseUrl(url: string): UrlTree { return this.urlSerializer.parse(url); } parseUrl(url: string): UrlTree { return this.urlSerializer.parse(url); }
private scheduleNavigation(url: UrlTree, pop: boolean):Promise<boolean> { private scheduleNavigation(url: UrlTree, pop: boolean): Promise<boolean> {
const id = ++ this.navigationId; const id = ++this.navigationId;
this.routerEvents.next(new NavigationStart(id, url)); this.routerEvents.next(new NavigationStart(id, url));
return Promise.resolve().then((_) => this.runNavigate(url, false, id)); return Promise.resolve().then((_) => this.runNavigate(url, false, id));
} }
@ -216,7 +226,7 @@ export class Router {
}); });
} }
private runNavigate(url: UrlTree, pop: boolean, id: number):Promise<boolean> { private runNavigate(url: UrlTree, pop: boolean, id: number): Promise<boolean> {
if (id !== this.navigationId) { if (id !== this.navigationId) {
this.routerEvents.next(new NavigationCancel(id, url)); this.routerEvents.next(new NavigationCancel(id, url));
return Promise.resolve(false); return Promise.resolve(false);
@ -224,69 +234,85 @@ export class Router {
return new Promise((resolvePromise, rejectPromise) => { return new Promise((resolvePromise, rejectPromise) => {
let state; let state;
recognize(this.rootComponentType, this.config, url).mergeMap((newRouterStateSnapshot) => { recognize(this.rootComponentType, this.config, url)
return resolve(this.resolver, newRouterStateSnapshot); .mergeMap((newRouterStateSnapshot) => {
return resolve(this.resolver, newRouterStateSnapshot);
}).map((routerStateSnapshot) => { })
return createRouterState(routerStateSnapshot, this.currentRouterState); .map((routerStateSnapshot) => {
return createRouterState(routerStateSnapshot, this.currentRouterState);
}).map((newState:RouterState) => { })
state = newState; .map((newState: RouterState) => {
state = newState;
}).mergeMap(_ => { })
return new GuardChecks(state.snapshot, this.currentRouterState.snapshot, this.injector).check(this.outletMap); .mergeMap(_ => {
return new GuardChecks(state.snapshot, this.currentRouterState.snapshot, this.injector)
.check(this.outletMap);
}).forEach((shouldActivate) => { })
if (!shouldActivate || id !== this.navigationId) { .forEach((shouldActivate) => {
this.routerEvents.next(new NavigationCancel(id, url)); if (!shouldActivate || id !== this.navigationId) {
return Promise.resolve(false); this.routerEvents.next(new NavigationCancel(id, url));
} return Promise.resolve(false);
}
new ActivateRoutes(state, this.currentRouterState).activate(this.outletMap); new ActivateRoutes(state, this.currentRouterState).activate(this.outletMap);
this.currentUrlTree = url; this.currentUrlTree = url;
this.currentRouterState = state; this.currentRouterState = state;
if (!pop) { if (!pop) {
this.location.go(this.urlSerializer.serialize(url)); this.location.go(this.urlSerializer.serialize(url));
} }
}).then(() => { })
this.routerEvents.next(new NavigationEnd(id, url)); .then(
resolvePromise(true); () => {
this.routerEvents.next(new NavigationEnd(id, url));
resolvePromise(true);
}, e => { },
this.routerEvents.next(new NavigationError(id, url, e)); e => {
rejectPromise(e); this.routerEvents.next(new NavigationError(id, url, e));
}); rejectPromise(e);
});
}); });
} }
} }
class CanActivate { constructor(public route: ActivatedRouteSnapshot) {}} class CanActivate {
class CanDeactivate { constructor(public component: Object, public route: ActivatedRouteSnapshot) {}} constructor(public route: ActivatedRouteSnapshot) {}
}
class CanDeactivate {
constructor(public component: Object, public route: ActivatedRouteSnapshot) {}
}
class GuardChecks { class GuardChecks {
private checks = []; private checks = [];
constructor(private future: RouterStateSnapshot, private curr: RouterStateSnapshot, private injector: Injector) {} constructor(
private future: RouterStateSnapshot, private curr: RouterStateSnapshot,
private injector: Injector) {}
check(parentOutletMap: RouterOutletMap): Observable<boolean> { check(parentOutletMap: RouterOutletMap): Observable<boolean> {
const futureRoot = this.future._root; const futureRoot = this.future._root;
const currRoot = this.curr ? this.curr._root : null; const currRoot = this.curr ? this.curr._root : null;
this.traverseChildRoutes(futureRoot, currRoot, parentOutletMap); this.traverseChildRoutes(futureRoot, currRoot, parentOutletMap);
if (this.checks.length === 0) return of(true); if (this.checks.length === 0) return of (true);
return forkJoin(this.checks.map(s => { return forkJoin(this.checks.map(s => {
if (s instanceof CanActivate) { if (s instanceof CanActivate) {
return this.runCanActivate(s.route); return this.runCanActivate(s.route);
} else if (s instanceof CanDeactivate) { } else if (s instanceof CanDeactivate) {
return this.runCanDeactivate(s.component, s.route); return this.runCanDeactivate(s.component, s.route);
} else { } else {
throw new Error("Cannot be reached"); throw new Error('Cannot be reached');
} }
})).map(and); }))
.map(and);
} }
private traverseChildRoutes(futureNode: TreeNode<ActivatedRouteSnapshot>, private traverseChildRoutes(
currNode: TreeNode<ActivatedRouteSnapshot> | null, futureNode: TreeNode<ActivatedRouteSnapshot>, currNode: TreeNode<ActivatedRouteSnapshot>|null,
outletMap: RouterOutletMap | null): void { outletMap: RouterOutletMap|null): void {
const prevChildren = nodeChildrenAsMap(currNode); const prevChildren = nodeChildrenAsMap(currNode);
futureNode.children.forEach(c => { futureNode.children.forEach(c => {
this.traverseRoutes(c, prevChildren[c.value.outlet], outletMap); this.traverseRoutes(c, prevChildren[c.value.outlet], outletMap);
@ -295,8 +321,9 @@ class GuardChecks {
forEach(prevChildren, (v, k) => this.deactivateOutletAndItChildren(v, outletMap._outlets[k])); forEach(prevChildren, (v, k) => this.deactivateOutletAndItChildren(v, outletMap._outlets[k]));
} }
traverseRoutes(futureNode: TreeNode<ActivatedRouteSnapshot>, currNode: TreeNode<ActivatedRouteSnapshot> | null, traverseRoutes(
parentOutletMap: RouterOutletMap | null): void { futureNode: TreeNode<ActivatedRouteSnapshot>, currNode: TreeNode<ActivatedRouteSnapshot>|null,
parentOutletMap: RouterOutletMap|null): void {
const future = futureNode.value; const future = futureNode.value;
const curr = currNode ? currNode.value : null; const curr = currNode ? currNode.value : null;
const outlet = parentOutletMap ? parentOutletMap._outlets[futureNode.value.outlet] : null; const outlet = parentOutletMap ? parentOutletMap._outlets[futureNode.value.outlet] : null;
@ -315,35 +342,39 @@ class GuardChecks {
private deactivateOutletAndItChildren(route: ActivatedRouteSnapshot, outlet: RouterOutlet): void { private deactivateOutletAndItChildren(route: ActivatedRouteSnapshot, outlet: RouterOutlet): void {
if (outlet && outlet.isActivated) { if (outlet && outlet.isActivated) {
forEach(outlet.outletMap._outlets, (v, k) => this.deactivateOutletAndItChildren(v, outlet.outletMap._outlets[k])); forEach(
outlet.outletMap._outlets,
(v, k) => this.deactivateOutletAndItChildren(v, outlet.outletMap._outlets[k]));
this.checks.push(new CanDeactivate(outlet.component, route)); this.checks.push(new CanDeactivate(outlet.component, route));
} }
} }
private runCanActivate(future: ActivatedRouteSnapshot): Observable<boolean> { private runCanActivate(future: ActivatedRouteSnapshot): Observable<boolean> {
const canActivate = future._routeConfig ? future._routeConfig.canActivate : null; const canActivate = future._routeConfig ? future._routeConfig.canActivate : null;
if (!canActivate || canActivate.length === 0) return of(true); if (!canActivate || canActivate.length === 0) return of (true);
return forkJoin(canActivate.map(c => { return forkJoin(canActivate.map(c => {
const guard = this.injector.get(c); const guard = this.injector.get(c);
if (guard.canActivate) { if (guard.canActivate) {
return wrapIntoObservable(guard.canActivate(future, this.future)); return wrapIntoObservable(guard.canActivate(future, this.future));
} else { } else {
return wrapIntoObservable(guard(future, this.future)); return wrapIntoObservable(guard(future, this.future));
} }
})).map(and); }))
.map(and);
} }
private runCanDeactivate(component: Object, curr: ActivatedRouteSnapshot): Observable<boolean> { private runCanDeactivate(component: Object, curr: ActivatedRouteSnapshot): Observable<boolean> {
const canDeactivate = curr._routeConfig ? curr._routeConfig.canDeactivate : null; const canDeactivate = curr._routeConfig ? curr._routeConfig.canDeactivate : null;
if (!canDeactivate || canDeactivate.length === 0) return of(true); if (!canDeactivate || canDeactivate.length === 0) return of (true);
return forkJoin(canDeactivate.map(c => { return forkJoin(canDeactivate.map(c => {
const guard = this.injector.get(c); const guard = this.injector.get(c);
if (guard.canDeactivate) { if (guard.canDeactivate) {
return wrapIntoObservable(guard.canDeactivate(component, curr, this.curr)); return wrapIntoObservable(guard.canDeactivate(component, curr, this.curr));
} else { } else {
return wrapIntoObservable(guard(component, curr, this.curr)); return wrapIntoObservable(guard(component, curr, this.curr));
} }
})).map(and); }))
.map(and);
} }
} }
@ -351,14 +382,14 @@ function wrapIntoObservable<T>(value: T | Observable<T>): Observable<T> {
if (value instanceof Observable) { if (value instanceof Observable) {
return value; return value;
} else { } else {
return of(value); return of (value);
} }
} }
class ActivateRoutes { class ActivateRoutes {
constructor(private futureState: RouterState, private currState: RouterState) {} constructor(private futureState: RouterState, private currState: RouterState) {}
activate(parentOutletMap: RouterOutletMap):void { activate(parentOutletMap: RouterOutletMap): void {
const futureRoot = this.futureState._root; const futureRoot = this.futureState._root;
const currRoot = this.currState ? this.currState._root : null; const currRoot = this.currState ? this.currState._root : null;
@ -366,9 +397,9 @@ class ActivateRoutes {
this.activateChildRoutes(futureRoot, currRoot, parentOutletMap); this.activateChildRoutes(futureRoot, currRoot, parentOutletMap);
} }
private activateChildRoutes(futureNode: TreeNode<ActivatedRoute>, private activateChildRoutes(
currNode: TreeNode<ActivatedRoute> | null, futureNode: TreeNode<ActivatedRoute>, currNode: TreeNode<ActivatedRoute>|null,
outletMap: RouterOutletMap): void { outletMap: RouterOutletMap): void {
const prevChildren = nodeChildrenAsMap(currNode); const prevChildren = nodeChildrenAsMap(currNode);
futureNode.children.forEach(c => { futureNode.children.forEach(c => {
this.activateRoutes(c, prevChildren[c.value.outlet], outletMap); this.activateRoutes(c, prevChildren[c.value.outlet], outletMap);
@ -377,8 +408,9 @@ class ActivateRoutes {
forEach(prevChildren, (v, k) => this.deactivateOutletAndItChildren(outletMap._outlets[k])); forEach(prevChildren, (v, k) => this.deactivateOutletAndItChildren(outletMap._outlets[k]));
} }
activateRoutes(futureNode: TreeNode<ActivatedRoute>, currNode: TreeNode<ActivatedRoute> | null, activateRoutes(
parentOutletMap: RouterOutletMap): void { futureNode: TreeNode<ActivatedRoute>, currNode: TreeNode<ActivatedRoute>|null,
parentOutletMap: RouterOutletMap): void {
const future = futureNode.value; const future = futureNode.value;
const curr = currNode ? currNode.value : null; const curr = currNode ? currNode.value : null;
const outlet = getOutlet(parentOutletMap, futureNode.value); const outlet = getOutlet(parentOutletMap, futureNode.value);
@ -394,7 +426,8 @@ class ActivateRoutes {
} }
} }
private activateNewRoutes(outletMap: RouterOutletMap, future: ActivatedRoute, outlet: RouterOutlet): void { private activateNewRoutes(
outletMap: RouterOutletMap, future: ActivatedRoute, outlet: RouterOutlet): void {
const resolved = ReflectiveInjector.resolve([ const resolved = ReflectiveInjector.resolve([
{provide: ActivatedRoute, useValue: future}, {provide: ActivatedRoute, useValue: future},
{provide: RouterOutletMap, useValue: outletMap} {provide: RouterOutletMap, useValue: outletMap}
@ -421,15 +454,11 @@ function pushQueryParamsAndFragment(state: RouterState): void {
} }
} }
function nodeChildrenAsMap(node: TreeNode<any>|null) { function nodeChildrenAsMap(node: TreeNode<any>| null) {
return node ? return node ? node.children.reduce((m, c) => {
node.children.reduce( m[c.value.outlet] = c;
(m, c) => { return m;
m[c.value.outlet] = c; }, {}) : {};
return m;
},
{}) :
{};
} }
function getOutlet(outletMap: RouterOutletMap, route: ActivatedRoute): RouterOutlet { function getOutlet(outletMap: RouterOutletMap, route: ActivatedRoute): RouterOutlet {

View File

@ -1,4 +1,4 @@
import { RouterOutlet } from './directives/router_outlet'; import {RouterOutlet} from './directives/router_outlet';
/** /**
* @internal * @internal

View File

@ -1,7 +1,9 @@
import { RouterConfig } from './config';
import * as common from './common_router_providers';
import {BrowserPlatformLocation} from '@angular/platform-browser';
import {PlatformLocation} from '@angular/common'; import {PlatformLocation} from '@angular/common';
import {BrowserPlatformLocation} from '@angular/platform-browser';
import * as common from './common_router_providers';
import {RouterConfig} from './config';
/** /**
* A list of {@link Provider}s. To use the router, you must add this to your application. * A list of {@link Provider}s. To use the router, you must add this to your application.
@ -13,7 +15,7 @@ import {PlatformLocation} from '@angular/common';
* class AppCmp { * class AppCmp {
* // ... * // ...
* } * }
* *
* const router = [ * const router = [
* {path: '/home', component: Home} * {path: '/home', component: Home}
* ]; * ];
@ -21,9 +23,8 @@ import {PlatformLocation} from '@angular/common';
* bootstrap(AppCmp, [provideRouter(router)]); * bootstrap(AppCmp, [provideRouter(router)]);
* ``` * ```
*/ */
export function provideRouter(config: RouterConfig):any[] { export function provideRouter(config: RouterConfig): any[] {
return [ return [
{provide: PlatformLocation, useClass: BrowserPlatformLocation}, {provide: PlatformLocation, useClass: BrowserPlatformLocation}, ...common.provideRouter(config)
...common.provideRouter(config)
]; ];
} }

View File

@ -1,11 +1,13 @@
import { Tree, TreeNode } from './utils/tree'; import {ComponentFactory, Type} from '@angular/core';
import { shallowEqual } from './utils/collection'; import {BehaviorSubject} from 'rxjs/BehaviorSubject';
import { UrlSegment } from './url_tree'; import {Observable} from 'rxjs/Observable';
import { Route } from './config';
import { Params, PRIMARY_OUTLET } from './shared'; import {Route} from './config';
import { Observable } from 'rxjs/Observable'; import {PRIMARY_OUTLET, Params} from './shared';
import { BehaviorSubject } from 'rxjs/BehaviorSubject'; import {UrlSegment} from './url_tree';
import { Type, ComponentFactory } from '@angular/core'; import {shallowEqual} from './utils/collection';
import {Tree, TreeNode} from './utils/tree';
/** /**
* The state of the router. * The state of the router.
@ -26,34 +28,41 @@ export class RouterState extends Tree<ActivatedRoute> {
/** /**
* @internal * @internal
*/ */
constructor(root: TreeNode<ActivatedRoute>, public queryParams: Observable<Params>, public fragment: Observable<string>, public snapshot: RouterStateSnapshot) { constructor(
root: TreeNode<ActivatedRoute>, public queryParams: Observable<Params>,
public fragment: Observable<string>, public snapshot: RouterStateSnapshot) {
super(root); super(root);
} }
} }
export function createEmptyState(rootComponent: Type): RouterState { export function createEmptyState(rootComponent: Type): RouterState {
const snapshot = createEmptyStateSnapshot(rootComponent); const snapshot = createEmptyStateSnapshot(rootComponent);
const emptyUrl = new BehaviorSubject([new UrlSegment("", {}, PRIMARY_OUTLET)]); const emptyUrl = new BehaviorSubject([new UrlSegment('', {}, PRIMARY_OUTLET)]);
const emptyParams = new BehaviorSubject({}); const emptyParams = new BehaviorSubject({});
const emptyQueryParams = new BehaviorSubject({}); const emptyQueryParams = new BehaviorSubject({});
const fragment = new BehaviorSubject(""); const fragment = new BehaviorSubject('');
const activated = new ActivatedRoute(emptyUrl, emptyParams, PRIMARY_OUTLET, rootComponent, snapshot.root); const activated =
new ActivatedRoute(emptyUrl, emptyParams, PRIMARY_OUTLET, rootComponent, snapshot.root);
activated.snapshot = snapshot.root; activated.snapshot = snapshot.root;
return new RouterState(new TreeNode<ActivatedRoute>(activated, []), emptyQueryParams, fragment, snapshot); return new RouterState(
new TreeNode<ActivatedRoute>(activated, []), emptyQueryParams, fragment, snapshot);
} }
function createEmptyStateSnapshot(rootComponent: Type): RouterStateSnapshot { function createEmptyStateSnapshot(rootComponent: Type): RouterStateSnapshot {
const rootUrlSegment = new UrlSegment("", {}, PRIMARY_OUTLET); const rootUrlSegment = new UrlSegment('', {}, PRIMARY_OUTLET);
const emptyUrl = [rootUrlSegment]; const emptyUrl = [rootUrlSegment];
const emptyParams = {}; const emptyParams = {};
const emptyQueryParams = {}; const emptyQueryParams = {};
const fragment = ""; const fragment = '';
const activated = new ActivatedRouteSnapshot(emptyUrl, emptyParams, PRIMARY_OUTLET, rootComponent, null, rootUrlSegment); const activated = new ActivatedRouteSnapshot(
return new RouterStateSnapshot(new TreeNode<ActivatedRouteSnapshot>(activated, []), emptyQueryParams, fragment); emptyUrl, emptyParams, PRIMARY_OUTLET, rootComponent, null, rootUrlSegment);
return new RouterStateSnapshot(
new TreeNode<ActivatedRouteSnapshot>(activated, []), emptyQueryParams, fragment);
} }
/** /**
* Contains the information about a component loaded in an outlet. The information is provided through * Contains the information about a component loaded in an outlet. The information is provided
* through
* the params and urlSegments observables. * the params and urlSegments observables.
* *
* ### Usage * ### Usage
@ -74,12 +83,10 @@ export class ActivatedRoute {
/** /**
* @internal * @internal
*/ */
constructor(public urlSegments: Observable<UrlSegment[]>, constructor(
public params: Observable<Params>, public urlSegments: Observable<UrlSegment[]>, public params: Observable<Params>,
public outlet: string, public outlet: string, public component: Type|string,
public component: Type | string, futureSnapshot: ActivatedRouteSnapshot) {
futureSnapshot: ActivatedRouteSnapshot
) {
this._futureSnapshot = futureSnapshot; this._futureSnapshot = futureSnapshot;
} }
} }
@ -102,9 +109,9 @@ export class ActivatedRouteSnapshot {
* @internal * @internal
*/ */
_resolvedComponentFactory: ComponentFactory<any>; _resolvedComponentFactory: ComponentFactory<any>;
/** @internal **/ /** @internal **/
_routeConfig: Route | null; _routeConfig: Route|null;
/** @internal **/ /** @internal **/
_lastUrlSegment: UrlSegment; _lastUrlSegment: UrlSegment;
@ -112,12 +119,9 @@ export class ActivatedRouteSnapshot {
/** /**
* @internal * @internal
*/ */
constructor(public urlSegments: UrlSegment[], constructor(
public params: Params, public urlSegments: UrlSegment[], public params: Params, public outlet: string,
public outlet: string, public component: Type|string, routeConfig: Route|null, lastUrlSegment: UrlSegment) {
public component: Type | string,
routeConfig: Route | null,
lastUrlSegment: UrlSegment) {
this._routeConfig = routeConfig; this._routeConfig = routeConfig;
this._lastUrlSegment = lastUrlSegment; this._lastUrlSegment = lastUrlSegment;
} }
@ -140,7 +144,9 @@ export class RouterStateSnapshot extends Tree<ActivatedRouteSnapshot> {
/** /**
* @internal * @internal
*/ */
constructor(root: TreeNode<ActivatedRouteSnapshot>, public queryParams: Params, public fragment: string | null) { constructor(
root: TreeNode<ActivatedRouteSnapshot>, public queryParams: Params,
public fragment: string|null) {
super(root); super(root);
} }
} }

View File

@ -2,9 +2,11 @@
* Name of the primary outlet. * Name of the primary outlet.
* @type {string} * @type {string}
*/ */
export const PRIMARY_OUTLET: string = "PRIMARY_OUTLET"; export const PRIMARY_OUTLET = 'PRIMARY_OUTLET';
/** /**
* A collection of parameters. * A collection of parameters.
*/ */
export type Params = { [key: string]: string }; export type Params = {
[key: string]: string
};

View File

@ -1,6 +1,7 @@
import { UrlTree, UrlSegment } from './url_tree'; import {PRIMARY_OUTLET} from './shared';
import { PRIMARY_OUTLET } from './shared'; import {UrlSegment, UrlTree} from './url_tree';
import { TreeNode } from './utils/tree'; import {TreeNode} from './utils/tree';
/** /**
* Defines a way to serialize/deserialize a url tree. * Defines a way to serialize/deserialize a url tree.
@ -26,7 +27,7 @@ export class DefaultUrlSerializer implements UrlSerializer {
return new UrlTree(p.parseRootSegment(), p.parseQueryParams(), p.parseFragment()); return new UrlTree(p.parseRootSegment(), p.parseQueryParams(), p.parseFragment());
} }
serialize(tree: UrlTree): string { serialize(tree: UrlTree): string {
const node = serializeUrlTreeNode(tree._root); const node = serializeUrlTreeNode(tree._root);
const query = serializeQueryParams(tree.queryParams); const query = serializeQueryParams(tree.queryParams);
const fragment = tree.fragment !== null ? `#${tree.fragment}` : ''; const fragment = tree.fragment !== null ? `#${tree.fragment}` : '';
@ -41,7 +42,8 @@ function serializeUrlTreeNode(node: TreeNode<UrlSegment>): string {
function serializeUrlTreeNodes(nodes: TreeNode<UrlSegment>[]): string { function serializeUrlTreeNodes(nodes: TreeNode<UrlSegment>[]): string {
const primary = serializeSegment(nodes[0].value); const primary = serializeSegment(nodes[0].value);
const secondaryNodes = nodes.slice(1); const secondaryNodes = nodes.slice(1);
const secondary = secondaryNodes.length > 0 ? `(${secondaryNodes.map(serializeUrlTreeNode).join("//")})` : ""; const secondary =
secondaryNodes.length > 0 ? `(${secondaryNodes.map(serializeUrlTreeNode).join("//")})` : '';
const children = serializeChildren(nodes[0]); const children = serializeChildren(nodes[0]);
return `${primary}${secondary}${children}`; return `${primary}${secondary}${children}`;
} }
@ -50,7 +52,7 @@ function serializeChildren(node: TreeNode<UrlSegment>): string {
if (node.children.length > 0) { if (node.children.length > 0) {
return `/${serializeUrlTreeNodes(node.children)}`; return `/${serializeUrlTreeNodes(node.children)}`;
} else { } else {
return ""; return '';
} }
} }
@ -60,16 +62,18 @@ export function serializeSegment(segment: UrlSegment): string {
} }
function serializeParams(params: {[key: string]: string}): string { function serializeParams(params: {[key: string]: string}): string {
return pairs(params).map(p => `;${p.first}=${p.second}`).join(""); return pairs(params).map(p => `;${p.first}=${p.second}`).join('');
} }
function serializeQueryParams(params: {[key: string]: string}): string { function serializeQueryParams(params: {[key: string]: string}): string {
const strs = pairs(params).map(p => `${p.first}=${p.second}`); const strs = pairs(params).map(p => `${p.first}=${p.second}`);
return strs.length > 0 ? `?${strs.join("&")}` : ""; return strs.length > 0 ? `?${strs.join("&")}` : '';
} }
class Pair<A,B> { constructor(public first:A, public second:B) {} } class Pair<A, B> {
function pairs<T>(obj: {[key: string]: T}):Pair<string,T>[] { constructor(public first: A, public second: B) {}
}
function pairs<T>(obj: {[key: string]: T}): Pair<string, T>[] {
const res = []; const res = [];
for (let prop in obj) { for (let prop in obj) {
if (obj.hasOwnProperty(prop)) { if (obj.hasOwnProperty(prop)) {
@ -106,7 +110,7 @@ class UrlParser {
} }
parseRootSegment(): TreeNode<UrlSegment> { parseRootSegment(): TreeNode<UrlSegment> {
if (this.remaining == '' || this.remaining == '/') { if (this.remaining == '' || this.remaining == '/') {
return new TreeNode<UrlSegment>(new UrlSegment('', {}, PRIMARY_OUTLET), []); return new TreeNode<UrlSegment>(new UrlSegment('', {}, PRIMARY_OUTLET), []);
} else { } else {
const segments = this.parseSegments(false); const segments = this.parseSegments(false);
@ -126,17 +130,17 @@ class UrlParser {
let outletName; let outletName;
if (hasOutletName) { if (hasOutletName) {
if (path.indexOf(":") === -1) { if (path.indexOf(':') === -1) {
throw new Error("Not outlet name is provided"); throw new Error('Not outlet name is provided');
} }
if (path.indexOf(":") > -1 && hasOutletName) { if (path.indexOf(':') > -1 && hasOutletName) {
let parts = path.split(":"); let parts = path.split(':');
outletName = parts[0]; outletName = parts[0];
path = parts[1]; path = parts[1];
} }
} else { } else {
if (path.indexOf(":") > -1) { if (path.indexOf(':') > -1) {
throw new Error("Not outlet name is allowed"); throw new Error('Not outlet name is allowed');
} }
outletName = PRIMARY_OUTLET; outletName = PRIMARY_OUTLET;
} }
@ -175,7 +179,7 @@ class UrlParser {
return params; return params;
} }
parseFragment(): string | null { parseFragment(): string|null {
if (this.peekStartsWith('#')) { if (this.peekStartsWith('#')) {
return this.remaining.substring(1); return this.remaining.substring(1);
} else { } else {
@ -198,7 +202,7 @@ class UrlParser {
return; return;
} }
this.capture(key); this.capture(key);
var value: any = "true"; var value: any = 'true';
if (this.peekStartsWith('=')) { if (this.peekStartsWith('=')) {
this.capture('='); this.capture('=');
var valueMatch = matchUrlSegment(this.remaining); var valueMatch = matchUrlSegment(this.remaining);
@ -217,7 +221,7 @@ class UrlParser {
return; return;
} }
this.capture(key); this.capture(key);
var value: any = "true"; var value: any = 'true';
if (this.peekStartsWith('=')) { if (this.peekStartsWith('=')) {
this.capture('='); this.capture('=');
var valueMatch = matchUrlQueryParamValue(this.remaining); var valueMatch = matchUrlQueryParamValue(this.remaining);

View File

@ -1,9 +1,10 @@
import { Tree, TreeNode } from './utils/tree'; import {PRIMARY_OUTLET} from './shared';
import { shallowEqual } from './utils/collection'; import {shallowEqual} from './utils/collection';
import { PRIMARY_OUTLET } from './shared'; import {Tree, TreeNode} from './utils/tree';
export function createEmptyUrlTree() { export function createEmptyUrlTree() {
return new UrlTree(new TreeNode<UrlSegment>(new UrlSegment("", {}, PRIMARY_OUTLET), []), {}, null); return new UrlTree(
new TreeNode<UrlSegment>(new UrlSegment('', {}, PRIMARY_OUTLET), []), {}, null);
} }
/** /**
@ -13,7 +14,9 @@ export class UrlTree extends Tree<UrlSegment> {
/** /**
* @internal * @internal
*/ */
constructor(root: TreeNode<UrlSegment>, public queryParams: {[key: string]: string}, public fragment: string | null) { constructor(
root: TreeNode<UrlSegment>, public queryParams: {[key: string]: string},
public fragment: string|null) {
super(root); super(root);
} }
} }
@ -22,7 +25,8 @@ export class UrlSegment {
/** /**
* @internal * @internal
*/ */
constructor(public path: string, public parameters: {[key: string]: string}, public outlet: string) {} constructor(
public path: string, public parameters: {[key: string]: string}, public outlet: string) {}
toString() { toString() {
const params = []; const params = [];

View File

@ -1,4 +1,4 @@
export function shallowEqual(a: {[x:string]:any}, b: {[x:string]:any}): boolean { export function shallowEqual(a: {[x: string]: any}, b: {[x: string]: any}): boolean {
var k1 = Object.keys(a); var k1 = Object.keys(a);
var k2 = Object.keys(b); var k2 = Object.keys(b);
if (k1.length != k2.length) { if (k1.length != k2.length) {
@ -24,12 +24,12 @@ export function flatten<T>(a: T[][]): T[] {
return target; return target;
} }
export function first<T>(a: T[]): T | null { export function first<T>(a: T[]): T|null {
return a.length > 0 ? a[0] : null; return a.length > 0 ? a[0] : null;
} }
export function and(bools: boolean[]): boolean { export function and(bools: boolean[]): boolean {
return bools.reduce((a,b) => a && b, true); return bools.reduce((a, b) => a && b, true);
} }
export function merge<V>(m1: {[key: string]: V}, m2: {[key: string]: V}): {[key: string]: V} { export function merge<V>(m1: {[key: string]: V}, m2: {[key: string]: V}): {[key: string]: V} {
@ -50,7 +50,8 @@ export function merge<V>(m1: {[key: string]: V}, m2: {[key: string]: V}): {[key:
return m; return m;
} }
export function forEach<K, V>(map: {[key: string]: V}, callback: /*(V, K) => void*/ Function): void { export function forEach<K, V>(
map: {[key: string]: V}, callback: /*(V, K) => void*/ Function): void {
for (var prop in map) { for (var prop in map) {
if (map.hasOwnProperty(prop)) { if (map.hasOwnProperty(prop)) {
callback(map[prop], prop); callback(map[prop], prop);

View File

@ -6,7 +6,7 @@ export class Tree<T> {
get root(): T { return this._root.value; } get root(): T { return this._root.value; }
parent(t: T): T | null { parent(t: T): T|null {
const p = this.pathFromRoot(t); const p = this.pathFromRoot(t);
return p.length > 1 ? p[p.length - 2] : null; return p.length > 1 ? p[p.length - 2] : null;
} }
@ -16,7 +16,7 @@ export class Tree<T> {
return n ? n.children.map(t => t.value) : []; return n ? n.children.map(t => t.value) : [];
} }
firstChild(t: T): T | null { firstChild(t: T): T|null {
const n = findNode(t, this._root); const n = findNode(t, this._root);
return n && n.children.length > 0 ? n.children[0].value : null; return n && n.children.length > 0 ? n.children[0].value : null;
} }
@ -34,7 +34,7 @@ export class Tree<T> {
contains(tree: Tree<T>): boolean { return contains(this._root, tree._root); } contains(tree: Tree<T>): boolean { return contains(this._root, tree._root); }
} }
function findNode<T>(expected: T, c: TreeNode<T>): TreeNode<T> | null { function findNode<T>(expected: T, c: TreeNode<T>): TreeNode<T>|null {
if (expected === c.value) return c; if (expected === c.value) return c;
for (let cc of c.children) { for (let cc of c.children) {
const r = findNode(expected, cc); const r = findNode(expected, cc);

View File

@ -1,7 +1,6 @@
{ {
"rulesDirectory": ["node_modules/codelyzer"], "rulesDirectory": ["node_modules/codelyzer"],
"rules": { "rules": {
"max-line-length": [true, 100],
"no-inferrable-types": true, "no-inferrable-types": true,
"class-name": true, "class-name": true,
"comment-format": [ "comment-format": [
@ -12,14 +11,13 @@
true, true,
"spaces" "spaces"
], ],
"eofline": true,
"no-duplicate-variable": true, "no-duplicate-variable": true,
"no-eval": true, "no-eval": true,
"no-arg": true, "no-arg": true,
"no-internal-module": true, "no-internal-module": true,
"no-trailing-whitespace": true, "no-trailing-whitespace": true,
"no-bitwise": true, "no-bitwise": true,
"no-shadowed-variable": true, "no-shadowed-variable": false,
"no-unused-expression": true, "no-unused-expression": true,
"no-unused-variable": true, "no-unused-variable": true,
"one-line": [ "one-line": [
@ -29,11 +27,6 @@
"check-open-brace", "check-open-brace",
"check-whitespace" "check-whitespace"
], ],
"quotemark": [
true,
"single",
"avoid-escape"
],
"semicolon": [true, "always"], "semicolon": [true, "always"],
"typedef-whitespace": [ "typedef-whitespace": [
true, true,
@ -45,12 +38,12 @@
"variable-declaration": "nospace" "variable-declaration": "nospace"
} }
], ],
"curly": true, "curly": false,
"variable-name": [ "variable-name": [
true, true,
"ban-keywords", "ban-keywords",
"check-format", "check-format",
"allow-trailing-underscore" "allow-leading-underscore"
], ],
"whitespace": [ "whitespace": [
true, true,
@ -62,10 +55,6 @@
], ],
"component-selector-name": [true, "kebab-case"], "component-selector-name": [true, "kebab-case"],
"component-selector-type": [true, "element"], "component-selector-type": [true, "element"],
"host-parameter-decorator": true,
"input-parameter-decorator": true,
"output-parameter-decorator": true,
"attribute-parameter-decorator": true,
"input-property-directive": true, "input-property-directive": true,
"output-property-directive": true "output-property-directive": true
} }