X Tutup
Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions modules/angular2/src/facade/lang.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class Math {
static final _random = new math.Random();
static int floor(num n) => n.floor();
static double random() => _random.nextDouble();
static num min(num a, num b) => math.min(a, b);
}

class CONST {
Expand Down
6 changes: 3 additions & 3 deletions modules/angular2/src/router/instruction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,8 @@ export abstract class Instruction {

get urlParams(): string[] { return isPresent(this.component) ? this.component.urlParams : []; }

get specificity(): number {
var total = 0;
get specificity(): string {
var total = '';
if (isPresent(this.component)) {
total += this.component.specificity;
}
Expand Down Expand Up @@ -305,7 +305,7 @@ export class ComponentInstruction {
public routeData: RouteData;

constructor(public urlPath: string, public urlParams: string[], data: RouteData,
public componentType, public terminal: boolean, public specificity: number,
public componentType, public terminal: boolean, public specificity: string,
public params: {[key: string]: any} = null) {
this.routeData = isPresent(data) ? data : BLANK_ROUTE_DATA;
}
Expand Down
38 changes: 19 additions & 19 deletions modules/angular2/src/router/path_recognizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,45 +96,45 @@ function parsePathString(route: string): {[key: string]: any} {

var segments = splitBySlash(route);
var results = [];
var specificity = 0;

var specificity = '';

// a single slash (or "empty segment" is as specific as a static segment
if (segments.length == 0) {
specificity += '2';
}

// The "specificity" of a path is used to determine which route is used when multiple routes match
// a URL.
// Static segments (like "/foo") are the most specific, followed by dynamic segments (like
// "/:id"). Star segments
// add no specificity. Segments at the start of the path are more specific than proceeding ones.
// a URL. Static segments (like "/foo") are the most specific, followed by dynamic segments (like
// "/:id"). Star segments add no specificity. Segments at the start of the path are more specific
// than proceeding ones.
//
// The code below uses place values to combine the different types of segments into a single
// integer that we can
// sort later. Each static segment is worth hundreds of points of specificity (10000, 9900, ...,
// 200), and each
// dynamic segment is worth single points of specificity (100, 99, ... 2).
if (segments.length > 98) {
throw new BaseException(`'${route}' has more than the maximum supported number of segments.`);
}
// string that we can sort later. Each static segment is marked as a specificity of "2," each
// dynamic segment is worth "1" specificity, and stars are worth "0" specificity.

var limit = segments.length - 1;
for (var i = 0; i <= limit; i++) {
var segment = segments[i], match;

if (isPresent(match = RegExpWrapper.firstMatch(paramMatcher, segment))) {
results.push(new DynamicSegment(match[1]));
specificity += (100 - i);
specificity += '1';
} else if (isPresent(match = RegExpWrapper.firstMatch(wildcardMatcher, segment))) {
results.push(new StarSegment(match[1]));
specificity += '0';
} else if (segment == '...') {
if (i < limit) {
throw new BaseException(`Unexpected "..." before the end of the path for "${route}".`);
}
results.push(new ContinuationSegment());
} else {
results.push(new StaticSegment(segment));
specificity += 100 * (100 - i);
specificity += '2';
}
}
var result = StringMapWrapper.create();
StringMapWrapper.set(result, 'segments', results);
StringMapWrapper.set(result, 'specificity', specificity);
return result;

return {'segments': results, 'specificity': specificity};
}

// this function is used to determine whether a route config path like `/foo/:id` collides with
Expand Down Expand Up @@ -177,7 +177,7 @@ function assertPath(path: string) {
*/
export class PathRecognizer {
private _segments: Segment[];
specificity: number;
specificity: string;
terminal: boolean = true;
hash: string;

Expand Down
2 changes: 1 addition & 1 deletion modules/angular2/src/router/route_recognizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export class RedirectRecognizer implements AbstractRecognizer {

// represents something like '/foo/:bar'
export class RouteRecognizer implements AbstractRecognizer {
specificity: number;
specificity: string;
terminal: boolean = true;
hash: string;

Expand Down
36 changes: 35 additions & 1 deletion modules/angular2/src/router/route_registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import {
isString,
isStringMap,
Type,
StringWrapper,
Math,
getTypeNameForDebugging,
CONST_EXPR
} from 'angular2/src/facade/lang';
Expand Down Expand Up @@ -492,7 +494,39 @@ function splitAndFlattenLinkParams(linkParams: any[]): any[] {
* Given a list of instructions, returns the most specific instruction
*/
function mostSpecific(instructions: Instruction[]): Instruction {
return ListWrapper.maximum(instructions, (instruction: Instruction) => instruction.specificity);
instructions = instructions.filter((instruction) => isPresent(instruction));
if (instructions.length == 0) {
return null;
}
if (instructions.length == 1) {
return instructions[0];
}
var first = instructions[0];
var rest = instructions.slice(1);
return rest.reduce((instruction: Instruction, contender: Instruction) => {
if (compareSpecificityStrings(contender.specificity, instruction.specificity) == -1) {
return contender;
}
return instruction;
}, first);
}

/*
* Expects strings to be in the form of "[0-2]+"
* Returns -1 if string A should be sorted above string B, 1 if it should be sorted after,
* or 0 if they are the same.
*/
function compareSpecificityStrings(a: string, b: string): number {
var l = Math.min(a.length, b.length);
for (var i = 0; i < l; i += 1) {
var ai = StringWrapper.charCodeAt(a, i);
var bi = StringWrapper.charCodeAt(b, i);
var difference = bi - ai;
if (difference != 0) {
return difference;
}
}
return a.length - b.length;
}

function assertTerminalComponent(component, path) {
Expand Down
19 changes: 19 additions & 0 deletions modules/angular2/test/router/route_registry_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,21 @@ export function main() {
});
}));

it('should prefer routes with high specificity over routes with children with lower specificity',
inject([AsyncTestCompleter], (async) => {
registry.config(RootHostCmp, new Route({path: '/first', component: DummyCmpA}));

// terminates to DummyCmpB
registry.config(RootHostCmp,
new Route({path: '/:second/...', component: SingleSlashChildCmp}));

registry.recognize('/first', [])
.then((instruction) => {
expect(instruction.component.componentType).toBe(DummyCmpA);
async.done();
});
}));

it('should match the full URL using child components', inject([AsyncTestCompleter], (async) => {
registry.config(RootHostCmp, new Route({path: '/first/...', component: DummyParentCmp}));

Expand Down Expand Up @@ -322,6 +337,10 @@ class DummyCmpB {}
class DefaultRouteCmp {
}

@RouteConfig([new Route({path: '/', component: DummyCmpB, name: 'ThirdCmp'})])
class SingleSlashChildCmp {
}


@RouteConfig([
new Route(
Expand Down
4 changes: 2 additions & 2 deletions modules/angular2/test/router/router_link_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ import {
import {DOM} from 'angular2/src/platform/dom/dom_adapter';
import {ResolvedInstruction} from 'angular2/src/router/instruction';

let dummyInstruction =
new ResolvedInstruction(new ComponentInstruction('detail', [], null, null, true, 0), null, {});
let dummyInstruction = new ResolvedInstruction(
new ComponentInstruction('detail', [], null, null, true, '0'), null, {});

export function main() {
describe('routerLink directive', function() {
Expand Down
X Tutup