refactor(router): rename candidate into snapshot

This commit is contained in:
vsavkin 2016-06-01 17:55:21 -07:00
parent c5cca8e098
commit 243612e36d
7 changed files with 89 additions and 62 deletions

View File

@ -1,18 +1,18 @@
import { RouterStateCandidate, ActivatedRouteCandidate, RouterState, ActivatedRoute } from './router_state';
import { RouterStateSnapshot, ActivatedRouteSnapshot, RouterState, ActivatedRoute } from './router_state';
import { TreeNode } from './utils/tree';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
export function createRouterState(curr: RouterStateCandidate, prevState: RouterState): RouterState {
export function createRouterState(curr: RouterStateSnapshot, prevState: RouterState): RouterState {
const root = createNode(curr._root, prevState ? prevState._root : null);
const queryParams = prevState ? prevState.queryParams : new BehaviorSubject(curr.queryParams);
const fragment = prevState ? prevState.fragment : new BehaviorSubject(curr.fragment);
return new RouterState(root, queryParams, fragment, curr);
}
function createNode(curr:TreeNode<ActivatedRouteCandidate>, prevState?:TreeNode<ActivatedRoute>):TreeNode<ActivatedRoute> {
if (prevState && equalRouteCandidates(prevState.value.candidate, curr.value)) {
function createNode(curr:TreeNode<ActivatedRouteSnapshot>, prevState?:TreeNode<ActivatedRoute>):TreeNode<ActivatedRoute> {
if (prevState && equalRouteSnapshots(prevState.value.snapshot, curr.value)) {
const value = prevState.value;
value.candidate = curr.value;
value.snapshot = curr.value;
const children = createOrReuseChildren(curr, prevState);
return new TreeNode<ActivatedRoute>(value, children);
@ -24,9 +24,9 @@ function createNode(curr:TreeNode<ActivatedRouteCandidate>, prevState?:TreeNode<
}
}
function createOrReuseChildren(curr:TreeNode<ActivatedRouteCandidate>, prevState:TreeNode<ActivatedRoute>) {
function createOrReuseChildren(curr:TreeNode<ActivatedRouteSnapshot>, prevState:TreeNode<ActivatedRoute>) {
return curr.children.map(child => {
const index = prevState.children.findIndex(p => equalRouteCandidates(p.value.candidate, 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,10 +35,10 @@ function createOrReuseChildren(curr:TreeNode<ActivatedRouteCandidate>, prevState
});
}
function createActivatedRoute(c:ActivatedRouteCandidate) {
function createActivatedRoute(c:ActivatedRouteSnapshot) {
return new ActivatedRoute(new BehaviorSubject(c.urlSegments), new BehaviorSubject(c.params), c.outlet, c.component, c);
}
function equalRouteCandidates(a: ActivatedRouteCandidate, b: ActivatedRouteCandidate): boolean {
function equalRouteSnapshots(a: ActivatedRouteSnapshot, b: ActivatedRouteSnapshot): boolean {
return a._routeConfig === b._routeConfig;
}

View File

@ -1,28 +1,28 @@
import { UrlTree, UrlSegment } from './url_tree';
import { flatten, first, merge } from './utils/collection';
import { TreeNode } from './utils/tree';
import { RouterStateCandidate, ActivatedRouteCandidate } from './router_state';
import { RouterStateSnapshot, ActivatedRouteSnapshot } from './router_state';
import { Params, PRIMARY_OUTLET } from './shared';
import { RouterConfig, Route } from './config';
import { Type } from '@angular/core';
import { Observable } from 'rxjs/Observable';
export function recognize(rootComponentType: Type, config: RouterConfig, url: UrlTree): Observable<RouterStateCandidate> {
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);
const roots = constructActivatedRoute(match);
const res = new RouterStateCandidate(roots[0], url.queryParameters, url.fragment);
return new Observable<RouterStateCandidate>(obs => {
const res = new RouterStateSnapshot(roots[0], url.queryParameters, url.fragment);
return new Observable<RouterStateSnapshot>(obs => {
obs.next(res);
obs.complete();
});
} catch(e) {
return new Observable<RouterStateCandidate>(obs => obs.error(e));
return new Observable<RouterStateSnapshot>(obs => obs.error(e));
}
}
function constructActivatedRoute(match: MatchResult): TreeNode<ActivatedRouteCandidate>[] {
const activatedRoute = createActivatedRouteCandidate(match);
function constructActivatedRoute(match: MatchResult): TreeNode<ActivatedRouteSnapshot>[] {
const activatedRoute = createActivatedRouteSnapshot(match);
if (match.leftOverUrl.length > 0) {
const children = recognizeMany(match.children, match.leftOverUrl);
checkOutletNameUniqueness(children);
@ -31,21 +31,21 @@ function constructActivatedRoute(match: MatchResult): TreeNode<ActivatedRouteCan
if (b.value.outlet === PRIMARY_OUTLET) return 1;
return a.value.outlet.localeCompare(b.value.outlet)
});
return [new TreeNode<ActivatedRouteCandidate>(activatedRoute, children)];
return [new TreeNode<ActivatedRouteSnapshot>(activatedRoute, children)];
} else {
return [new TreeNode<ActivatedRouteCandidate>(activatedRoute, [])];
return [new TreeNode<ActivatedRouteSnapshot>(activatedRoute, [])];
}
}
function recognizeMany(config: Route[], urls: TreeNode<UrlSegment>[]): TreeNode<ActivatedRouteCandidate>[] {
function recognizeMany(config: Route[], urls: TreeNode<UrlSegment>[]): TreeNode<ActivatedRouteSnapshot>[] {
return flatten(urls.map(url => recognizeOne(config, url)));
}
function createActivatedRouteCandidate(match: MatchResult): ActivatedRouteCandidate {
return new ActivatedRouteCandidate(match.consumedUrlSegments, match.parameters, match.outlet, match.component, match.route);
function createActivatedRouteSnapshot(match: MatchResult): ActivatedRouteSnapshot {
return new ActivatedRouteSnapshot(match.consumedUrlSegments, match.parameters, match.outlet, match.component, match.route);
}
function recognizeOne(config: Route[], url: TreeNode<UrlSegment>): TreeNode<ActivatedRouteCandidate>[] {
function recognizeOne(config: Route[], url: TreeNode<UrlSegment>): TreeNode<ActivatedRouteSnapshot>[] {
const m = match(config, url);
const primary = constructActivatedRoute(m);
const secondary = recognizeMany(config, m.secondary);
@ -54,7 +54,7 @@ function recognizeOne(config: Route[], url: TreeNode<UrlSegment>): TreeNode<Acti
return res;
}
function checkOutletNameUniqueness(nodes: TreeNode<ActivatedRouteCandidate>[]): TreeNode<ActivatedRouteCandidate>[] {
function checkOutletNameUniqueness(nodes: TreeNode<ActivatedRouteSnapshot>[]): TreeNode<ActivatedRouteSnapshot>[] {
let names = {};
nodes.forEach(n => {
let routeWithSameOutletName = names[n.value.outlet];

View File

@ -1,4 +1,4 @@
import { RouterStateCandidate, ActivatedRouteCandidate } from './router_state';
import { RouterStateSnapshot, ActivatedRouteSnapshot } from './router_state';
import { TreeNode } from './utils/tree';
import { ComponentResolver } from '@angular/core';
import { Observable } from 'rxjs/Observable';
@ -7,11 +7,11 @@ import {forkJoin} from 'rxjs/observable/forkJoin';
import {fromPromise} from 'rxjs/observable/fromPromise';
import 'rxjs/add/operator/toPromise';
export function resolve(resolver: ComponentResolver, state: RouterStateCandidate): Observable<RouterStateCandidate> {
export function resolve(resolver: ComponentResolver, state: RouterStateSnapshot): Observable<RouterStateSnapshot> {
return resolveNode(resolver, state._root).map(_ => state);
}
function resolveNode(resolver: ComponentResolver, node: TreeNode<ActivatedRouteCandidate>): 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;

View File

@ -8,7 +8,7 @@ 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, RouterStateCandidate, ActivatedRoute, ActivatedRouteCandidate} from './router_state';
import { createEmptyState, RouterState, RouterStateSnapshot, ActivatedRoute, ActivatedRouteSnapshot} from './router_state';
import { RouterConfig } from './config';
import { RouterOutlet } from './directives/router_outlet';
import { createUrlTree } from './create_url_tree';
@ -162,17 +162,17 @@ export class Router {
private runNavigate(url:UrlTree, pop?:boolean):Observable<any> {
let state;
const r = recognize(this.rootComponentType, this.config, url).mergeMap((newRouterStateCandidate) => {
return resolve(this.resolver, newRouterStateCandidate);
const r = recognize(this.rootComponentType, this.config, url).mergeMap((newRouterStateSnapshot) => {
return resolve(this.resolver, newRouterStateSnapshot);
}).map((routerStateCandidate) => {
return createRouterState(routerStateCandidate, this.currentRouterState);
}).map((routerStateSnapshot) => {
return createRouterState(routerStateSnapshot, this.currentRouterState);
}).map((newState:RouterState) => {
state = newState;
}).mergeMap(_ => {
return new GuardChecks(state.candidate, this.currentRouterState.candidate, this.injector).check(this.outletMap);
return new GuardChecks(state.snapshot, this.currentRouterState.snapshot, this.injector).check(this.outletMap);
});
r.subscribe((shouldActivate) => {
@ -192,7 +192,7 @@ export class Router {
class GuardChecks {
private checks = [];
constructor(private future: RouterStateCandidate, private curr: RouterStateCandidate, private injector: Injector) {}
constructor(private future: RouterStateSnapshot, private curr: RouterStateSnapshot, private injector: Injector) {}
check(parentOutletMap: RouterOutletMap): Observable<boolean> {
const futureRoot = this.future._root;
@ -202,8 +202,8 @@ class GuardChecks {
return forkJoin(this.checks.map(s => this.runCanActivate(s))).map(and);
}
private traverseChildRoutes(futureNode: TreeNode<ActivatedRouteCandidate>,
currNode: TreeNode<ActivatedRouteCandidate> | null,
private traverseChildRoutes(futureNode: TreeNode<ActivatedRouteSnapshot>,
currNode: TreeNode<ActivatedRouteSnapshot> | null,
outletMap: RouterOutletMap): void {
const prevChildren = nodeChildrenAsMap(currNode);
futureNode.children.forEach(c => {
@ -213,7 +213,7 @@ class GuardChecks {
forEach(prevChildren, (v, k) => this.deactivateOutletAndItChildren(outletMap._outlets[k]));
}
traverseRoutes(futureNode: TreeNode<ActivatedRouteCandidate>, currNode: TreeNode<ActivatedRouteCandidate> | null,
traverseRoutes(futureNode: TreeNode<ActivatedRouteSnapshot>, currNode: TreeNode<ActivatedRouteSnapshot> | null,
parentOutletMap: RouterOutletMap): void {
const future = futureNode.value;
const curr = currNode ? currNode.value : null;
@ -232,7 +232,7 @@ class GuardChecks {
private deactivateOutletAndItChildren(outlet: RouterOutlet): void {}
private runCanActivate(future: ActivatedRouteCandidate): Observable<boolean> {
private runCanActivate(future: ActivatedRouteSnapshot): Observable<boolean> {
const canActivate = future._routeConfig ? future._routeConfig.canActivate : null;
if (!canActivate || canActivate.length === 0) return of(true);
return forkJoin(canActivate.map(c => {
@ -286,7 +286,7 @@ class ActivateRoutes {
{provide: ActivatedRoute, useValue: future},
{provide: RouterOutletMap, useValue: outletMap}
]);
outlet.activate(future.candidate._resolvedComponentFactory, resolved, outletMap);
outlet.activate(future.snapshot._resolvedComponentFactory, resolved, outletMap);
pushValues(future);
}
@ -299,19 +299,19 @@ class ActivateRoutes {
}
function pushValues(route: ActivatedRoute): void {
if (!shallowEqual(route.candidate.params, (<any>route.params).value)) {
(<any>route.urlSegments).next(route.candidate.urlSegments);
(<any>route.params).next(route.candidate.params);
if (!shallowEqual(route.snapshot.params, (<any>route.params).value)) {
(<any>route.urlSegments).next(route.snapshot.urlSegments);
(<any>route.params).next(route.snapshot.params);
}
}
function pushQueryParamsAndFragment(state: RouterState): void {
if (!shallowEqual(state.candidate.queryParams, (<any>state.queryParams).value)) {
(<any>state.queryParams).next(state.candidate.queryParams);
if (!shallowEqual(state.snapshot.queryParams, (<any>state.queryParams).value)) {
(<any>state.queryParams).next(state.snapshot.queryParams);
}
if (state.candidate.fragment !== (<any>state.fragment).value) {
(<any>state.fragment).next(state.candidate.fragment);
if (state.snapshot.fragment !== (<any>state.fragment).value) {
(<any>state.fragment).next(state.snapshot.fragment);
}
}

View File

@ -7,7 +7,7 @@ import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { Type, ComponentFactory } from '@angular/core';
/**
* The state of the router at a particular moment in time.
* The state of the router.
*
* ### Usage
*
@ -22,32 +22,33 @@ import { Type, ComponentFactory } from '@angular/core';
* ```
*/
export class RouterState extends Tree<ActivatedRoute> {
constructor(root: TreeNode<ActivatedRoute>, public queryParams: Observable<Params>, public fragment: Observable<string>, public candidate: RouterStateCandidate) {
constructor(root: TreeNode<ActivatedRoute>, public queryParams: Observable<Params>, public fragment: Observable<string>, public snapshot: RouterStateSnapshot) {
super(root);
}
}
export function createEmptyState(rootComponent: Type): RouterState {
const candidate = createEmptyStateCandidate(rootComponent);
const snapshot = createEmptyStateSnapshot(rootComponent);
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, candidate.root);
return new RouterState(new TreeNode<ActivatedRoute>(activated, []), emptyQueryParams, fragment, candidate);
const activated = new ActivatedRoute(emptyUrl, emptyParams, PRIMARY_OUTLET, rootComponent, snapshot.root);
return new RouterState(new TreeNode<ActivatedRoute>(activated, []), emptyQueryParams, fragment, snapshot);
}
function createEmptyStateCandidate(rootComponent: Type): RouterStateCandidate {
function createEmptyStateSnapshot(rootComponent: Type): RouterStateSnapshot {
const emptyUrl = [new UrlSegment("", {}, PRIMARY_OUTLET)];
const emptyParams = {};
const emptyQueryParams = {};
const fragment = "";
const activated = new ActivatedRouteCandidate(emptyUrl, emptyParams, PRIMARY_OUTLET, rootComponent, null);
return new RouterStateCandidate(new TreeNode<ActivatedRouteCandidate>(activated, []), emptyQueryParams, fragment);
const activated = new ActivatedRouteSnapshot(emptyUrl, emptyParams, PRIMARY_OUTLET, rootComponent, null);
return new RouterStateSnapshot(new TreeNode<ActivatedRouteSnapshot>(activated, []), emptyQueryParams, fragment);
}
/**
* Contains the information about a component loaded in an outlet.
* Contains the information about a component loaded in an outlet. The information is provided through
* the params and urlSegments observables.
*
* ### Usage
*
@ -64,11 +65,24 @@ export class ActivatedRoute {
public params: Observable<Params>,
public outlet: string,
public component: Type | string,
public candidate: ActivatedRouteCandidate
public snapshot: ActivatedRouteSnapshot
) {}
}
export class ActivatedRouteCandidate {
/**
* Contains the information about a component loaded in an outlet at a particular moment in time.
*
* ### Usage
*
* ```
* class MyComponent {
* constructor(route: ActivatedRoute) {
* const id: string = route.snapshot.params.id;
* }
* }
* ```
*/
export class ActivatedRouteSnapshot {
/**
* @internal
*/
@ -86,8 +100,21 @@ export class ActivatedRouteCandidate {
}
}
export class RouterStateCandidate extends Tree<ActivatedRouteCandidate> {
constructor(root: TreeNode<ActivatedRouteCandidate>, public queryParams: Params, public fragment: string) {
/**
* The state of the router at a particular moment in time.
*
* ### Usage
*
* ```
* class MyComponent {
* constructor(router: Router) {
* const snapshot = router.routerState.snapshot;
* }
* }
* ```
*/
export class RouterStateSnapshot extends Tree<ActivatedRouteSnapshot> {
constructor(root: TreeNode<ActivatedRouteSnapshot>, public queryParams: Params, public fragment: string) {
super(root);
}
}

View File

@ -1,7 +1,7 @@
import {DefaultUrlSerializer} from '../src/url_serializer';
import {UrlTree} from '../src/url_tree';
import {Params, PRIMARY_OUTLET} from '../src/shared';
import {ActivatedRoute, ActivatedRouteCandidate, RouterStateCandidate, createEmptyState} from '../src/router_state';
import {ActivatedRoute, ActivatedRouteSnapshot, RouterStateSnapshot, createEmptyState} from '../src/router_state';
import {createRouterState} from '../src/create_router_state';
import {recognize} from '../src/recognize';
import {RouterConfig} from '../src/config';
@ -44,7 +44,7 @@ describe('create router state', () => {
});
});
function createState(config: RouterConfig, url: string): RouterStateCandidate {
function createState(config: RouterConfig, url: string): RouterStateSnapshot {
let res;
recognize(RootComponent, config, tree(url)).forEach(s => res = s);
return res;

View File

@ -1,7 +1,7 @@
import {DefaultUrlSerializer} from '../src/url_serializer';
import {UrlTree} from '../src/url_tree';
import {Params, PRIMARY_OUTLET} from '../src/shared';
import {ActivatedRouteCandidate} from '../src/router_state';
import {ActivatedRouteSnapshot} from '../src/router_state';
import {recognize} from '../src/recognize';
describe('recognize', () => {
@ -196,7 +196,7 @@ describe('recognize', () => {
});
});
function checkActivatedRoute(actual: ActivatedRouteCandidate | null, url: string, params: Params, cmp: Function, outlet: string = PRIMARY_OUTLET):void {
function checkActivatedRoute(actual: ActivatedRouteSnapshot | null, url: string, params: Params, cmp: Function, outlet: string = PRIMARY_OUTLET):void {
if (actual === null) {
expect(actual).toBeDefined();
} else {