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 { UrlSerializer, DefaultUrlSerializer } from './url_serializer';
import { ActivatedRoute } from './router_state';
import { Router } from './router';
import { RouterConfig } from './config';
import { ComponentResolver, ApplicationRef, Injector, APP_INITIALIZER } from '@angular/core';
import { LocationStrategy, PathLocationStrategy, Location } from '@angular/common';
import {Location, LocationStrategy, PathLocationStrategy} from '@angular/common';
import {APP_INITIALIZER, ApplicationRef, ComponentResolver, Injector} from '@angular/core';
import {RouterConfig} from './config';
import {Router} from './router';
import {RouterOutletMap} from './router_outlet_map';
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.
@ -24,7 +26,7 @@ import { LocationStrategy, PathLocationStrategy, Location } from '@angular/commo
* bootstrap(AppCmp, [provideRouter(router)]);
* ```
*/
export function provideRouter(config: RouterConfig):any[] {
export function provideRouter(config: RouterConfig): any[] {
return [
Location,
{provide: LocationStrategy, useClass: PathLocationStrategy},
@ -34,20 +36,27 @@ export function provideRouter(config: RouterConfig):any[] {
provide: Router,
useFactory: (ref, resolver, urlSerializer, outletMap, location, injector) => {
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 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());
return r;
},
deps: [ApplicationRef, ComponentResolver, UrlSerializer, RouterOutletMap, Location, Injector]
deps:
[ApplicationRef, ComponentResolver, UrlSerializer, RouterOutletMap, Location, Injector]
},
RouterOutletMap,
{provide: ActivatedRoute, useFactory: (r) => r.routerState.root, deps: [Router]},
// 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 interface Route {
index?: boolean;
path?: string;
component: Type | string;
component: Type|string;
outlet?: string;
canActivate?: any[];
canDeactivate?: any[];

View File

@ -1,6 +1,7 @@
import { RouterStateSnapshot, ActivatedRouteSnapshot, RouterState, ActivatedRoute } from './router_state';
import { TreeNode } from './utils/tree';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
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 {
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);
}
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)) {
const value = prevState.value;
value._futureSnapshot = curr.value;
const children = createOrReuseChildren(curr, prevState);
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 => {
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) {
return createNode(child, prevState.children[index]);
} else {
@ -35,8 +39,9 @@ function createOrReuseChildren(curr:TreeNode<ActivatedRouteSnapshot>, prevState:
});
}
function createActivatedRoute(c:ActivatedRouteSnapshot) {
return new ActivatedRoute(new BehaviorSubject(c.urlSegments), new BehaviorSubject(c.params), c.outlet, c.component, c);
function createActivatedRoute(c: ActivatedRouteSnapshot) {
return new ActivatedRoute(
new BehaviorSubject(c.urlSegments), new BehaviorSubject(c.params), c.outlet, c.component, c);
}
function equalRouteSnapshots(a: ActivatedRouteSnapshot, b: ActivatedRouteSnapshot): boolean {

View File

@ -1,11 +1,12 @@
import { UrlTree, UrlSegment, equalUrlSegments } from './url_tree';
import { TreeNode } from './utils/tree';
import { forEach, shallowEqual } from './utils/collection';
import { RouterState, ActivatedRoute } from './router_state';
import { Params, PRIMARY_OUTLET } from './shared';
import {ActivatedRoute} from './router_state';
import {PRIMARY_OUTLET, Params} from './shared';
import {UrlSegment, UrlTree} from './url_tree';
import {forEach, shallowEqual} from './utils/collection';
import {TreeNode} from './utils/tree';
export function createUrlTree(route: ActivatedRoute, urlTree: UrlTree, commands: any[],
queryParams: Params | undefined, fragment: string | undefined): UrlTree {
export function createUrlTree(
route: ActivatedRoute, urlTree: UrlTree, commands: any[], queryParams: Params | undefined,
fragment: string | undefined): UrlTree {
if (commands.length === 0) {
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);
}
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 f = fragment ? fragment : urlTree.fragment;
return new UrlTree(root, q, f);
@ -32,16 +35,16 @@ function tree(root: TreeNode<UrlSegment>, urlTree: UrlTree, queryParams: Params
function navigateToRoot(normalizedChange: NormalizedNavigationCommands): boolean {
return normalizedChange.isAbsolute && normalizedChange.commands.length === 1 &&
normalizedChange.commands[0] == "/";
normalizedChange.commands[0] == '/';
}
class NormalizedNavigationCommands {
constructor(public isAbsolute: boolean, public numberOfDoubleDots: number,
public commands: any[]) {}
constructor(
public isAbsolute: boolean, public numberOfDoubleDots: number, public commands: any[]) {}
}
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);
}
@ -52,7 +55,7 @@ function normalizeCommands(commands: any[]): NormalizedNavigationCommands {
for (let i = 0; i < commands.length; ++i) {
const c = commands[i];
if (!(typeof c === "string")) {
if (!(typeof c === 'string')) {
res.push(c);
continue;
}
@ -63,11 +66,11 @@ function normalizeCommands(commands: any[]): NormalizedNavigationCommands {
// first exp is treated in a special way
if (i == 0) {
if (j == 0 && cc == ".") { // './a'
if (j == 0 && cc == '.') { // './a'
// skip it
} else if (j == 0 && cc == "") { // '/a'
} else if (j == 0 && cc == '') { // '/a'
isAbsolute = true;
} else if (cc == "..") { // '../a'
} else if (cc == '..') { // '../a'
numberOfDoubleDots++;
} else if (cc != '') {
res.push(cc);
@ -84,22 +87,23 @@ function normalizeCommands(commands: any[]): NormalizedNavigationCommands {
return new NormalizedNavigationCommands(isAbsolute, numberOfDoubleDots, res);
}
function findStartingNode(normalizedChange: NormalizedNavigationCommands, urlTree: UrlTree,
route: ActivatedRoute): TreeNode<UrlSegment> {
function findStartingNode(
normalizedChange: NormalizedNavigationCommands, urlTree: UrlTree,
route: ActivatedRoute): TreeNode<UrlSegment> {
if (normalizedChange.isAbsolute) {
return urlTree._root;
} else {
const urlSegment =
findUrlSegment(route, urlTree, normalizedChange.numberOfDoubleDots);
const urlSegment = findUrlSegment(route, urlTree, normalizedChange.numberOfDoubleDots);
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 path = urlTree.pathFromRoot(urlSegment);
if (path.length <= numberOfDoubleDots) {
throw new Error("Invalid number of '../'");
throw new Error('Invalid number of \'../\'');
}
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}'`);
}
function constructNewTree(node: TreeNode<UrlSegment>, original: TreeNode<UrlSegment>,
updated: TreeNode<UrlSegment>[]): TreeNode<UrlSegment> {
function constructNewTree(
node: TreeNode<UrlSegment>, original: TreeNode<UrlSegment>,
updated: TreeNode<UrlSegment>[]): TreeNode<UrlSegment> {
if (node === original) {
return new TreeNode<UrlSegment>(node.value, updated);
} else {
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 {
if (!(typeof commands[0] === "string")) return commands[0];
const parts = commands[0].toString().split(":");
if (!(typeof commands[0] === 'string')) return commands[0];
const parts = commands[0].toString().split(':');
return parts.length > 1 ? parts[1] : commands[0];
}
function getOutlet(commands: any[]): string {
if (!(typeof commands[0] === "string")) return PRIMARY_OUTLET;
const parts = commands[0].toString().split(":");
if (!(typeof commands[0] === 'string')) return PRIMARY_OUTLET;
const parts = commands[0].toString().split(':');
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 next = rest.length === 0 ? null : rest[0];
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);
}
function recurse(urlSegment: UrlSegment, node: TreeNode<UrlSegment> | null,
rest: any[]): TreeNode<UrlSegment> {
function recurse(
urlSegment: UrlSegment, node: TreeNode<UrlSegment>| null, rest: any[]): TreeNode<UrlSegment> {
if (rest.length === 0) {
return new TreeNode<UrlSegment>(urlSegment, []);
}

View File

@ -1,13 +1,9 @@
import {
Directive,
HostListener,
HostBinding,
Input,
OnChanges
} from '@angular/core';
import {Directive, HostBinding, HostListener, Input, OnChanges} from '@angular/core';
import {Router} from '../router';
import {ActivatedRoute} from '../router_state';
/**
* 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 {
@Input() target: string;
private commands: any[] = [];
@Input() queryParams: {[k:string]:any};
@Input() queryParams: {[k: string]: any};
@Input() fragment: string;
// the url displayed on the anchor element.
@ -49,7 +45,7 @@ export class RouterLink implements OnChanges {
constructor(private router: Router, private route: ActivatedRoute) {}
@Input()
set routerLink(data: any[] | string) {
set routerLink(data: any[]|string) {
if (Array.isArray(data)) {
this.commands = <any>data;
} else {
@ -57,30 +53,24 @@ export class RouterLink implements OnChanges {
}
}
ngOnChanges(changes:{}):any {
this.updateTargetUrlAndHref();
}
ngOnChanges(changes: {}): any { this.updateTargetUrlAndHref(); }
@HostListener("click")
@HostListener('click')
onClick(): boolean {
// If no target, or if target is _self, prevent default browser behavior
if (!(typeof this.target === "string") || this.target == '_self') {
this.router.navigate(this.commands, {
relativeTo: this.route,
queryParams: this.queryParams,
fragment: this.fragment
});
if (!(typeof this.target === 'string') || this.target == '_self') {
this.router.navigate(
this.commands,
{relativeTo: this.route, queryParams: this.queryParams, fragment: this.fragment});
return false;
}
return true;
}
private updateTargetUrlAndHref(): void {
const tree = this.router.createUrlTree(this.commands, {
relativeTo: this.route,
queryParams: this.queryParams,
fragment: this.fragment
});
const tree = this.router.createUrlTree(
this.commands,
{relativeTo: this.route, queryParams: this.queryParams, fragment: this.fragment});
if (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 {RouterOutletMap} from '../router_outlet_map';
import {PRIMARY_OUTLET} from '../shared';
import {Attribute, ComponentFactory, ComponentRef, Directive, ReflectiveInjector, ResolvedReflectiveProvider, ViewContainerRef} from "@angular/core";
import {RouterOutletMap} from "../router_outlet_map";
import {PRIMARY_OUTLET} from "../shared";
@Directive({selector: 'router-outlet'})
export class RouterOutlet {
private activated:ComponentRef<any>|null;
public outletMap:RouterOutletMap;
private activated: ComponentRef<any>|null;
public outletMap: RouterOutletMap;
/**
* @internal
*/
constructor(parentOutletMap:RouterOutletMap, private location:ViewContainerRef,
@Attribute('name') name:string) {
constructor(
parentOutletMap: RouterOutletMap, private location: ViewContainerRef,
@Attribute('name') name: string) {
parentOutletMap.registerOutlet(name ? name : PRIMARY_OUTLET, this);
}
get isActivated(): boolean { return !!this.activated; }
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;
}
@ -28,8 +29,9 @@ export class RouterOutlet {
}
}
activate(factory: ComponentFactory<any>, providers: ResolvedReflectiveProvider[],
outletMap: RouterOutletMap): void {
activate(
factory: ComponentFactory<any>, providers: ResolvedReflectiveProvider[],
outletMap: RouterOutletMap): void {
this.outletMap = outletMap;
const inj = ReflectiveInjector.fromResolvedProviders(providers, this.location.parentInjector);
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';
export { UrlSerializer, DefaultUrlSerializer } from './url_serializer';
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 {RouterLink} from './directives/router_link';
import {RouterOutlet} from './directives/router_outlet';
import { RouterOutlet } from './directives/router_outlet';
import { RouterLink } from './directives/router_link';
export {Route, RouterConfig} from './config';
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];

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.
*/
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.
*/
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 { flatten, first, merge } from './utils/collection';
import { TreeNode } from './utils/tree';
import { RouterStateSnapshot, ActivatedRouteSnapshot } from './router_state';
import { PRIMARY_OUTLET } from './shared';
import { RouterConfig, Route } from './config';
import { Type } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import {Type} from '@angular/core';
import {Observable} from 'rxjs/Observable';
import {Route, RouterConfig} from './config';
import {ActivatedRouteSnapshot, RouterStateSnapshot} from './router_state';
import {PRIMARY_OUTLET} from './shared';
import {UrlSegment, UrlTree} from './url_tree';
import {first, flatten, merge} from './utils/collection';
import {TreeNode} from './utils/tree';
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 {
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 res = new RouterStateSnapshot(roots[0], url.queryParams, url.fragment);
return new Observable<RouterStateSnapshot>(obs => {
obs.next(res);
obs.complete();
});
} catch(e) {
} catch (e) {
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 {
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>[] {
const activatedRoute = createActivatedRouteSnapshot(match);
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);
children.sort((a, b) => {
if (a.value.outlet === PRIMARY_OUTLET) return -1;
@ -40,23 +46,28 @@ function constructActivatedRoute(match: MatchResult): TreeNode<ActivatedRouteSna
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 [];
const mIndex = matchIndex(config, [], lastUrlSegment);
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)));
}
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);
for(let match of matches) {
for (let match of matches) {
try {
const primary = constructActivatedRoute(match);
const secondary = recognizeMany(config, match.secondary);
@ -64,7 +75,7 @@ function recognizeOne(config: Route[], url: TreeNode<UrlSegment>): TreeNode<Acti
checkOutletNameUniqueness(res);
return res;
} catch (e) {
if (! (e instanceof CannotRecognize)) {
if (!(e instanceof CannotRecognize)) {
throw e;
}
}
@ -72,13 +83,14 @@ function recognizeOne(config: Route[], url: TreeNode<UrlSegment>): TreeNode<Acti
throw new CannotRecognize();
}
function checkOutletNameUniqueness(nodes: TreeNode<ActivatedRouteSnapshot>[]): TreeNode<ActivatedRouteSnapshot>[] {
function checkOutletNameUniqueness(nodes: TreeNode<ActivatedRouteSnapshot>[]):
TreeNode<ActivatedRouteSnapshot>[] {
let names = {};
nodes.forEach(n => {
let routeWithSameOutletName = names[n.value.outlet];
if (routeWithSameOutletName) {
const p = routeWithSameOutletName.urlSegments.map(s => s.toString()).join("/");
const c = n.value.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('/');
throw new Error(`Two segments cannot have the same outlet name: '${p}' and '${c}'.`);
}
names[n.value.outlet] = n.value;
@ -99,13 +111,18 @@ function match(config: Route[], url: TreeNode<UrlSegment>): MatchResult[] {
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 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) {
if (r.index) {
return createIndexMatch(r, leftOverUrls, lastUrlSegment);
@ -114,23 +131,24 @@ function matchIndex(config: Route[], leftOverUrls: TreeNode<UrlSegment>[], lastU
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.outlet ? route.outlet : PRIMARY_OUTLET) !== url.value.outlet) return null;
const path = route.path.startsWith("/") ? route.path.substring(1) : route.path;
if (path === "**") {
const path = route.path.startsWith('/') ? route.path.substring(1) : route.path;
if (path === '**') {
const consumedUrl = [];
let u:TreeNode<UrlSegment>|null = url;
let u: TreeNode<UrlSegment>|null = url;
while (u) {
consumedUrl.push(u.value);
u = first(u.children);
}
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 consumedUrlSegments = [];
@ -144,7 +162,7 @@ function matchWithParts(route: Route, url: TreeNode<UrlSegment>): MatchResult |
const p = parts[i];
const isLastSegment = i === parts.length - 1;
const isLastParent = i === parts.length - 2;
const isPosParam = p.startsWith(":");
const isPosParam = p.startsWith(':');
if (!isPosParam && p != current.value.path) return null;
if (isLastSegment) {
@ -163,7 +181,7 @@ function matchWithParts(route: Route, url: TreeNode<UrlSegment>): MatchResult |
current = first(current.children);
}
if (!lastSegment) throw "Cannot be reached";
if (!lastSegment) throw 'Cannot be reached';
const p = lastSegment.value.parameters;
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 outlet = route.outlet ? route.outlet : PRIMARY_OUTLET;
return new MatchResult(route.component, children, consumedUrlSegments, parameters, lastSegment.children,
secondarySubtrees, outlet, route, lastSegment.value);
return new MatchResult(
route.component, children, consumedUrlSegments, parameters, lastSegment.children,
secondarySubtrees, outlet, route, lastSegment.value);
}
class MatchResult {
constructor(public component: Type | string,
public children: Route[],
public consumedUrlSegments: UrlSegment[],
public parameters: {[key: string]: string},
public leftOverUrl: TreeNode<UrlSegment>[],
public secondary: TreeNode<UrlSegment>[],
public outlet: string,
public route: Route | null,
public lastUrlSegment: UrlSegment
) {}
constructor(
public component: Type|string, public children: Route[],
public consumedUrlSegments: UrlSegment[], public parameters: {[key: string]: string},
public leftOverUrl: TreeNode<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 {forkJoin} from 'rxjs/observable/forkJoin';
import {fromPromise} from 'rxjs/observable/fromPromise';
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);
}
function resolveNode(resolver: ComponentResolver, node: TreeNode<ActivatedRouteSnapshot>): Observable<any> {
function resolveNode(
resolver: ComponentResolver, node: TreeNode<ActivatedRouteSnapshot>): Observable<any> {
if (node.children.length === 0) {
return fromPromise(resolver.resolveComponent(<any>node.value.component).then(factory => {
node.value._resolvedComponentFactory = factory;
return node.value;
}));
} else {
const c = node.children.map(c => resolveNode(resolver, c).toPromise());
return forkJoin(c).map(_ => resolver.resolveComponent(<any>node.value.component).then(factory => {
node.value._resolvedComponentFactory = factory;
return node.value;
}));
return forkJoin(c).map(
_ => resolver.resolveComponent(<any>node.value.component).then(factory => {
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/scan';
import 'rxjs/add/operator/mergeMap';
import 'rxjs/add/operator/concat';
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
*/
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
*/
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
*/
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
*/
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;
@ -61,7 +75,10 @@ export class Router {
/**
* @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.currentUrlTree = createEmptyUrlTree();
this.currentRouterState = createEmptyState(this.rootComponentType);
@ -70,7 +87,7 @@ export class Router {
/**
* @internal
*/
initialNavigation():void {
initialNavigation(): void {
this.setUpLocationChangeListener();
this.navigateByUrl(this.location.path());
}
@ -78,23 +95,17 @@ export class Router {
/**
* Returns the current route state.
*/
get routerState(): RouterState {
return this.currentRouterState;
}
get routerState(): RouterState { return this.currentRouterState; }
/**
* Returns the current url tree.
*/
get urlTree(): UrlTree {
return this.currentUrlTree;
}
get urlTree(): UrlTree { return this.currentUrlTree; }
/**
* Returns an observable of route events
*/
get events(): Observable<Event> {
return this.routerEvents;
}
get events(): Observable<Event> { return this.routerEvents; }
/**
* Navigate based on the provided url. This navigation is always absolute.
@ -129,9 +140,7 @@ export class Router {
* ]);
* ```
*/
resetConfig(config: RouterConfig): void {
this.config = config;
}
resetConfig(config: RouterConfig): void { this.config = config; }
/**
* @internal
@ -169,7 +178,8 @@ export class Router {
* 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;
return createUrlTree(a, this.currentUrlTree, commands, queryParams, fragment);
}
@ -204,8 +214,8 @@ export class Router {
*/
parseUrl(url: string): UrlTree { return this.urlSerializer.parse(url); }
private scheduleNavigation(url: UrlTree, pop: boolean):Promise<boolean> {
const id = ++ this.navigationId;
private scheduleNavigation(url: UrlTree, pop: boolean): Promise<boolean> {
const id = ++this.navigationId;
this.routerEvents.next(new NavigationStart(id, url));
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) {
this.routerEvents.next(new NavigationCancel(id, url));
return Promise.resolve(false);
@ -224,69 +234,85 @@ export class Router {
return new Promise((resolvePromise, rejectPromise) => {
let state;
recognize(this.rootComponentType, this.config, url).mergeMap((newRouterStateSnapshot) => {
return resolve(this.resolver, newRouterStateSnapshot);
recognize(this.rootComponentType, this.config, url)
.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) {
this.routerEvents.next(new NavigationCancel(id, url));
return Promise.resolve(false);
}
})
.forEach((shouldActivate) => {
if (!shouldActivate || id !== this.navigationId) {
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.currentRouterState = state;
if (!pop) {
this.location.go(this.urlSerializer.serialize(url));
}
}).then(() => {
this.routerEvents.next(new NavigationEnd(id, url));
resolvePromise(true);
this.currentUrlTree = url;
this.currentRouterState = state;
if (!pop) {
this.location.go(this.urlSerializer.serialize(url));
}
})
.then(
() => {
this.routerEvents.next(new NavigationEnd(id, url));
resolvePromise(true);
}, e => {
this.routerEvents.next(new NavigationError(id, url, e));
rejectPromise(e);
});
},
e => {
this.routerEvents.next(new NavigationError(id, url, e));
rejectPromise(e);
});
});
}
}
class CanActivate { constructor(public route: ActivatedRouteSnapshot) {}}
class CanDeactivate { constructor(public component: Object, public route: ActivatedRouteSnapshot) {}}
class CanActivate {
constructor(public route: ActivatedRouteSnapshot) {}
}
class CanDeactivate {
constructor(public component: Object, public route: ActivatedRouteSnapshot) {}
}
class GuardChecks {
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> {
const futureRoot = this.future._root;
const currRoot = this.curr ? this.curr._root : null;
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 => {
if (s instanceof CanActivate) {
return this.runCanActivate(s.route);
} else if (s instanceof CanDeactivate) {
return this.runCanDeactivate(s.component, s.route);
} else {
throw new Error("Cannot be reached");
}
})).map(and);
if (s instanceof CanActivate) {
return this.runCanActivate(s.route);
} else if (s instanceof CanDeactivate) {
return this.runCanDeactivate(s.component, s.route);
} else {
throw new Error('Cannot be reached');
}
}))
.map(and);
}
private traverseChildRoutes(futureNode: TreeNode<ActivatedRouteSnapshot>,
currNode: TreeNode<ActivatedRouteSnapshot> | null,
outletMap: RouterOutletMap | null): void {
private traverseChildRoutes(
futureNode: TreeNode<ActivatedRouteSnapshot>, currNode: TreeNode<ActivatedRouteSnapshot>|null,
outletMap: RouterOutletMap|null): void {
const prevChildren = nodeChildrenAsMap(currNode);
futureNode.children.forEach(c => {
this.traverseRoutes(c, prevChildren[c.value.outlet], outletMap);
@ -295,8 +321,9 @@ class GuardChecks {
forEach(prevChildren, (v, k) => this.deactivateOutletAndItChildren(v, outletMap._outlets[k]));
}
traverseRoutes(futureNode: TreeNode<ActivatedRouteSnapshot>, currNode: TreeNode<ActivatedRouteSnapshot> | null,
parentOutletMap: RouterOutletMap | null): void {
traverseRoutes(
futureNode: TreeNode<ActivatedRouteSnapshot>, currNode: TreeNode<ActivatedRouteSnapshot>|null,
parentOutletMap: RouterOutletMap|null): void {
const future = futureNode.value;
const curr = currNode ? currNode.value : null;
const outlet = parentOutletMap ? parentOutletMap._outlets[futureNode.value.outlet] : null;
@ -315,35 +342,39 @@ class GuardChecks {
private deactivateOutletAndItChildren(route: ActivatedRouteSnapshot, outlet: RouterOutlet): void {
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));
}
}
private runCanActivate(future: ActivatedRouteSnapshot): Observable<boolean> {
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 => {
const guard = this.injector.get(c);
if (guard.canActivate) {
return wrapIntoObservable(guard.canActivate(future, this.future));
} else {
return wrapIntoObservable(guard(future, this.future));
}
})).map(and);
const guard = this.injector.get(c);
if (guard.canActivate) {
return wrapIntoObservable(guard.canActivate(future, this.future));
} else {
return wrapIntoObservable(guard(future, this.future));
}
}))
.map(and);
}
private runCanDeactivate(component: Object, curr: ActivatedRouteSnapshot): Observable<boolean> {
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 => {
const guard = this.injector.get(c);
if (guard.canDeactivate) {
return wrapIntoObservable(guard.canDeactivate(component, curr, this.curr));
} else {
return wrapIntoObservable(guard(component, curr, this.curr));
}
})).map(and);
const guard = this.injector.get(c);
if (guard.canDeactivate) {
return wrapIntoObservable(guard.canDeactivate(component, curr, this.curr));
} else {
return wrapIntoObservable(guard(component, curr, this.curr));
}
}))
.map(and);
}
}
@ -351,14 +382,14 @@ function wrapIntoObservable<T>(value: T | Observable<T>): Observable<T> {
if (value instanceof Observable) {
return value;
} else {
return of(value);
return of (value);
}
}
class ActivateRoutes {
constructor(private futureState: RouterState, private currState: RouterState) {}
activate(parentOutletMap: RouterOutletMap):void {
activate(parentOutletMap: RouterOutletMap): void {
const futureRoot = this.futureState._root;
const currRoot = this.currState ? this.currState._root : null;
@ -366,9 +397,9 @@ class ActivateRoutes {
this.activateChildRoutes(futureRoot, currRoot, parentOutletMap);
}
private activateChildRoutes(futureNode: TreeNode<ActivatedRoute>,
currNode: TreeNode<ActivatedRoute> | null,
outletMap: RouterOutletMap): void {
private activateChildRoutes(
futureNode: TreeNode<ActivatedRoute>, currNode: TreeNode<ActivatedRoute>|null,
outletMap: RouterOutletMap): void {
const prevChildren = nodeChildrenAsMap(currNode);
futureNode.children.forEach(c => {
this.activateRoutes(c, prevChildren[c.value.outlet], outletMap);
@ -377,8 +408,9 @@ class ActivateRoutes {
forEach(prevChildren, (v, k) => this.deactivateOutletAndItChildren(outletMap._outlets[k]));
}
activateRoutes(futureNode: TreeNode<ActivatedRoute>, currNode: TreeNode<ActivatedRoute> | null,
parentOutletMap: RouterOutletMap): void {
activateRoutes(
futureNode: TreeNode<ActivatedRoute>, currNode: TreeNode<ActivatedRoute>|null,
parentOutletMap: RouterOutletMap): void {
const future = futureNode.value;
const curr = currNode ? currNode.value : null;
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([
{provide: ActivatedRoute, useValue: future},
{provide: RouterOutletMap, useValue: outletMap}
@ -421,15 +454,11 @@ function pushQueryParamsAndFragment(state: RouterState): void {
}
}
function nodeChildrenAsMap(node: TreeNode<any>|null) {
return node ?
node.children.reduce(
(m, c) => {
m[c.value.outlet] = c;
return m;
},
{}) :
{};
function nodeChildrenAsMap(node: TreeNode<any>| null) {
return node ? node.children.reduce((m, c) => {
m[c.value.outlet] = c;
return m;
}, {}) : {};
}
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

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 {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.
@ -13,7 +15,7 @@ import {PlatformLocation} from '@angular/common';
* class AppCmp {
* // ...
* }
*
*
* const router = [
* {path: '/home', component: Home}
* ];
@ -21,9 +23,8 @@ import {PlatformLocation} from '@angular/common';
* bootstrap(AppCmp, [provideRouter(router)]);
* ```
*/
export function provideRouter(config: RouterConfig):any[] {
export function provideRouter(config: RouterConfig): any[] {
return [
{provide: PlatformLocation, useClass: BrowserPlatformLocation},
...common.provideRouter(config)
{provide: PlatformLocation, useClass: BrowserPlatformLocation}, ...common.provideRouter(config)
];
}

View File

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

View File

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

View File

@ -1,9 +1,10 @@
import { Tree, TreeNode } from './utils/tree';
import { shallowEqual } from './utils/collection';
import { PRIMARY_OUTLET } from './shared';
import {PRIMARY_OUTLET} from './shared';
import {shallowEqual} from './utils/collection';
import {Tree, TreeNode} from './utils/tree';
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
*/
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);
}
}
@ -22,7 +25,8 @@ export class UrlSegment {
/**
* @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() {
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 k2 = Object.keys(b);
if (k1.length != k2.length) {
@ -24,12 +24,12 @@ export function flatten<T>(a: T[][]): T[] {
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;
}
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} {
@ -50,7 +50,8 @@ export function merge<V>(m1: {[key: string]: V}, m2: {[key: string]: V}): {[key:
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) {
if (map.hasOwnProperty(prop)) {
callback(map[prop], prop);

View File

@ -6,7 +6,7 @@ export class Tree<T> {
get root(): T { return this._root.value; }
parent(t: T): T | null {
parent(t: T): T|null {
const p = this.pathFromRoot(t);
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) : [];
}
firstChild(t: T): T | null {
firstChild(t: T): T|null {
const n = findNode(t, this._root);
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); }
}
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;
for (let cc of c.children) {
const r = findNode(expected, cc);

View File

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