fix(router): allow router-link to link to redirects
Closes #3335 Closes #3624
This commit is contained in:
parent
b5c4d8ba79
commit
72e0b8f7dc
|
@ -216,17 +216,17 @@ export class PathRecognizer {
|
|||
var captured = [];
|
||||
|
||||
for (var i = 0; i < this._segments.length; i += 1) {
|
||||
if (isBlank(nextSegment)) {
|
||||
return null;
|
||||
}
|
||||
currentSegment = nextSegment;
|
||||
|
||||
var segment = this._segments[i];
|
||||
|
||||
currentSegment = nextSegment;
|
||||
if (segment instanceof ContinuationSegment) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (isBlank(currentSegment)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
captured.push(currentSegment.path);
|
||||
|
||||
// the star segment consumes all of the remaining URL, including matrix params
|
||||
|
@ -251,18 +251,26 @@ export class PathRecognizer {
|
|||
|
||||
var urlPath = captured.join('/');
|
||||
|
||||
// If this is the root component, read query params. Otherwise, read matrix params.
|
||||
var paramsSegment = beginningSegment instanceof RootUrl ? beginningSegment : currentSegment;
|
||||
var auxiliary;
|
||||
var instruction: ComponentInstruction;
|
||||
if (isPresent(currentSegment)) {
|
||||
// If this is the root component, read query params. Otherwise, read matrix params.
|
||||
var paramsSegment = beginningSegment instanceof RootUrl ? beginningSegment : currentSegment;
|
||||
|
||||
var allParams = isPresent(paramsSegment.params) ?
|
||||
StringMapWrapper.merge(paramsSegment.params, positionalParams) :
|
||||
positionalParams;
|
||||
|
||||
var allParams = isPresent(paramsSegment.params) ?
|
||||
StringMapWrapper.merge(paramsSegment.params, positionalParams) :
|
||||
positionalParams;
|
||||
var urlParams = serializeParams(paramsSegment.params);
|
||||
var urlParams = serializeParams(paramsSegment.params);
|
||||
|
||||
var instruction = new ComponentInstruction(urlPath, urlParams, this, allParams);
|
||||
instruction = new ComponentInstruction(urlPath, urlParams, this, allParams);
|
||||
|
||||
return new PathMatch(instruction, nextSegment, currentSegment.auxiliary);
|
||||
auxiliary = currentSegment.auxiliary;
|
||||
} else {
|
||||
instruction = new ComponentInstruction(urlPath, [], this, positionalParams);
|
||||
auxiliary = [];
|
||||
}
|
||||
return new PathMatch(instruction, nextSegment, auxiliary);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ import {
|
|||
import {reflector} from 'angular2/src/reflection/reflection';
|
||||
import {Injectable} from 'angular2/di';
|
||||
import {normalizeRouteConfig} from './route_config_nomalizer';
|
||||
import {parser, Url} from './url_parser';
|
||||
import {parser, Url, pathSegmentsToUrl} from './url_parser';
|
||||
|
||||
var _resolveToNull = PromiseWrapper.resolve(null);
|
||||
|
||||
|
@ -236,7 +236,8 @@ export class RouteRegistry {
|
|||
componentCursor = response.componentType;
|
||||
}
|
||||
|
||||
var instruction = null;
|
||||
var instruction: Instruction = this._generateRedirects(componentCursor);
|
||||
|
||||
|
||||
while (segments.length > 0) {
|
||||
instruction = new Instruction(segments.pop(), instruction, {});
|
||||
|
@ -244,6 +245,38 @@ export class RouteRegistry {
|
|||
|
||||
return instruction;
|
||||
}
|
||||
|
||||
// if the child includes a redirect like : "/" -> "/something",
|
||||
// we want to honor that redirection when creating the link
|
||||
private _generateRedirects(componentCursor: Type): Instruction {
|
||||
if (isBlank(componentCursor)) {
|
||||
return null;
|
||||
}
|
||||
var componentRecognizer = this._rules.get(componentCursor);
|
||||
if (isBlank(componentRecognizer)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (let i = 0; i < componentRecognizer.redirects.length; i += 1) {
|
||||
let redirect = componentRecognizer.redirects[i];
|
||||
|
||||
// we only handle redirecting from an empty segment
|
||||
if (redirect.segments.length == 1 && redirect.segments[0] == '') {
|
||||
var toSegments = pathSegmentsToUrl(redirect.toSegments);
|
||||
var matches = componentRecognizer.recognize(toSegments);
|
||||
var primaryInstruction =
|
||||
ListWrapper.maximum(matches, (match: PathMatch) => match.instruction.specificity);
|
||||
|
||||
if (isPresent(primaryInstruction)) {
|
||||
var child = this._generateRedirects(primaryInstruction.instruction.componentType);
|
||||
return new Instruction(primaryInstruction.instruction, child, {});
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -251,20 +284,8 @@ export class RouteRegistry {
|
|||
* Given a list of instructions, returns the most specific instruction
|
||||
*/
|
||||
function mostSpecific(instructions: List<PrimaryInstruction>): PrimaryInstruction {
|
||||
if (instructions.length == 0) {
|
||||
return null;
|
||||
}
|
||||
var mostSpecificSolution = instructions[0];
|
||||
for (var solutionIndex = 1; solutionIndex < instructions.length; solutionIndex++) {
|
||||
var solution: PrimaryInstruction = instructions[solutionIndex];
|
||||
if (isBlank(solution)) {
|
||||
continue;
|
||||
}
|
||||
if (solution.component.specificity > mostSpecificSolution.component.specificity) {
|
||||
mostSpecificSolution = solution;
|
||||
}
|
||||
}
|
||||
return mostSpecificSolution;
|
||||
return ListWrapper.maximum(
|
||||
instructions, (instruction: PrimaryInstruction) => instruction.component.specificity);
|
||||
}
|
||||
|
||||
function assertTerminalComponent(component, path) {
|
||||
|
|
|
@ -57,6 +57,14 @@ export class RootUrl extends Url {
|
|||
}
|
||||
}
|
||||
|
||||
export function pathSegmentsToUrl(pathSegments: List<string>): Url {
|
||||
var url = new Url(pathSegments[pathSegments.length - 1]);
|
||||
for (var i = pathSegments.length - 2; i >= 0; i -= 1) {
|
||||
url = new Url(pathSegments[i], url);
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
var SEGMENT_RE = RegExpWrapper.create('^[^\\/\\(\\)\\?;=&]+');
|
||||
function matchUrlSegment(str: string): string {
|
||||
var match = RegExpWrapper.firstMatch(SEGMENT_RE, str);
|
||||
|
|
|
@ -14,7 +14,13 @@ import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
|
|||
import {Type} from 'angular2/src/facade/lang';
|
||||
|
||||
import {RouteRegistry} from 'angular2/src/router/route_registry';
|
||||
import {RouteConfig, Route, AuxRoute, AsyncRoute} from 'angular2/src/router/route_config_decorator';
|
||||
import {
|
||||
RouteConfig,
|
||||
Route,
|
||||
Redirect,
|
||||
AuxRoute,
|
||||
AsyncRoute
|
||||
} from 'angular2/src/router/route_config_decorator';
|
||||
import {stringifyInstruction} from 'angular2/src/router/instruction';
|
||||
import {IS_DART} from '../platform';
|
||||
|
||||
|
@ -45,6 +51,24 @@ export function main() {
|
|||
.toEqual('second');
|
||||
});
|
||||
|
||||
it('should generate URLs that account for redirects', () => {
|
||||
registry.config(
|
||||
RootHostCmp,
|
||||
new Route({path: '/first/...', component: DummyParentRedirectCmp, as: 'firstCmp'}));
|
||||
|
||||
expect(stringifyInstruction(registry.generate(['firstCmp'], RootHostCmp)))
|
||||
.toEqual('first/second');
|
||||
});
|
||||
|
||||
it('should generate URLs in a hierarchy of redirects', () => {
|
||||
registry.config(
|
||||
RootHostCmp,
|
||||
new Route({path: '/first/...', component: DummyMultipleRedirectCmp, as: 'firstCmp'}));
|
||||
|
||||
expect(stringifyInstruction(registry.generate(['firstCmp'], RootHostCmp)))
|
||||
.toEqual('first/second/third');
|
||||
});
|
||||
|
||||
it('should generate URLs with params', () => {
|
||||
registry.config(
|
||||
RootHostCmp,
|
||||
|
@ -255,6 +279,28 @@ class DummyAsyncCmp {
|
|||
class DummyCmpA {}
|
||||
class DummyCmpB {}
|
||||
|
||||
@RouteConfig([
|
||||
new Redirect({path: '/', redirectTo: '/third'}),
|
||||
new Route({path: '/third', component: DummyCmpB, as: 'thirdCmp'})
|
||||
])
|
||||
class DummyRedirectCmp {
|
||||
}
|
||||
|
||||
|
||||
@RouteConfig([
|
||||
new Redirect({path: '/', redirectTo: '/second'}),
|
||||
new Route({path: '/second/...', component: DummyRedirectCmp, as: 'secondCmp'})
|
||||
])
|
||||
class DummyMultipleRedirectCmp {
|
||||
}
|
||||
|
||||
@RouteConfig([
|
||||
new Redirect({path: '/', redirectTo: '/second'}),
|
||||
new Route({path: '/second', component: DummyCmpB, as: 'secondCmp'})
|
||||
])
|
||||
class DummyParentRedirectCmp {
|
||||
}
|
||||
|
||||
@RouteConfig([new Route({path: '/second', component: DummyCmpB, as: 'secondCmp'})])
|
||||
class DummyParentCmp {
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue