Skip to content

Commit

Permalink
fix(router): properly read and serialize query params
Browse files Browse the repository at this point in the history
This splits out `path` and `query` into separate params for `location.go`
and related methods so that we can handle them properly in both `PathLocationStrategy`
and `HashLocationStrategy`.

This handles the problem of not reading query params to populate `Location` on the
initial page load.

Closes #3957
Closes #4225
Closes #3784
  • Loading branch information
btford committed Oct 13, 2015
1 parent 440fd11 commit 8bc40d3
Show file tree
Hide file tree
Showing 9 changed files with 60 additions and 27 deletions.
13 changes: 9 additions & 4 deletions modules/angular2/src/mock/location_mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ export class SpyLocation implements Location {
/** @internal */
_path: string = '';
/** @internal */
_query: string = '';
/** @internal */
_subject: EventEmitter = new EventEmitter();
/** @internal */
_baseHref: string = '';
Expand All @@ -21,12 +23,15 @@ export class SpyLocation implements Location {

normalizeAbsolutely(url: string): string { return this._baseHref + url; }

go(url: string) {
url = this.normalizeAbsolutely(url);
if (this._path == url) {
go(path: string, query: string = '') {
path = this.normalizeAbsolutely(path);
if (this._path == path && this._query == query) {
return;
}
this._path = url;
this._path = path;
this._query = query;

var url = path + (query.length > 0 ? ('?' + query) : '');
this.urlChanges.push(url);
}

Expand Down
4 changes: 3 additions & 1 deletion modules/angular2/src/mock/mock_location_strategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@ export class MockLocationStrategy extends LocationStrategy {
ObservableWrapper.callNext(this._subject, {'url': pathname});
}

pushState(ctx: any, title: string, url: string): void {
pushState(ctx: any, title: string, path: string, query: string): void {
this.internalTitle = title;

var url = path + (query.length > 0 ? ('?' + query) : '');
this.internalPath = url;
this.urlChanges.push(url);
}
Expand Down
15 changes: 11 additions & 4 deletions modules/angular2/src/router/hash_location_strategy.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {DOM} from 'angular2/src/core/dom/dom_adapter';
import {Injectable} from 'angular2/angular2';
import {LocationStrategy} from './location_strategy';
import {LocationStrategy, normalizeQueryParams} from './location_strategy';
import {EventListener, History, Location} from 'angular2/src/core/facade/browser';

/**
Expand Down Expand Up @@ -61,11 +61,18 @@ export class HashLocationStrategy extends LocationStrategy {
// Dart will complain if a call to substring is
// executed with a position value that extends the
// length of string.
return path.length > 0 ? path.substring(1) : path;
return (path.length > 0 ? path.substring(1) : path) +
normalizeQueryParams(this._location.search);
}

pushState(state: any, title: string, url: string) {
this._history.pushState(state, title, '#' + url);
pushState(state: any, title: string, path: string, queryParams: string) {
var url = path + normalizeQueryParams(queryParams);
if (url.length == 0) {
url = this._location.pathname;
} else {
url = '#' + url;
}
this._history.pushState(state, title, url);
}

forward(): void { this._history.forward(); }
Expand Down
14 changes: 10 additions & 4 deletions modules/angular2/src/router/instruction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,12 +94,18 @@ export class PrimaryInstruction {
}

export function stringifyInstruction(instruction: Instruction): string {
var params = instruction.component.urlParams.length > 0 ?
('?' + instruction.component.urlParams.join('&')) :
'';
return stringifyInstructionPath(instruction) + stringifyInstructionQuery(instruction);
}

export function stringifyInstructionPath(instruction: Instruction): string {
return instruction.component.urlPath + stringifyAux(instruction) +
stringifyPrimary(instruction.child) + params;
stringifyPrimary(instruction.child);
}

export function stringifyInstructionQuery(instruction: Instruction): string {
return instruction.component.urlParams.length > 0 ?
('?' + instruction.component.urlParams.join('&')) :
'';
}

function stringifyPrimary(instruction: Instruction): string {
Expand Down
6 changes: 3 additions & 3 deletions modules/angular2/src/router/location.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,9 @@ export class Location {
* Changes the browsers URL to the normalized version of the given URL, and pushes a
* new item onto the platform's history.
*/
go(url: string): void {
var finalUrl = this.normalizeAbsolutely(url);
this.platformStrategy.pushState(null, '', finalUrl);
go(path: string, query: string = ''): void {
var absolutePath = this.normalizeAbsolutely(path);
this.platformStrategy.pushState(null, '', absolutePath, query);
}

/**
Expand Down
6 changes: 5 additions & 1 deletion modules/angular2/src/router/location_strategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,13 @@
*/
export abstract class LocationStrategy {
abstract path(): string;
abstract pushState(ctx: any, title: string, url: string): void;
abstract pushState(state: any, title: string, url: string, queryParams: string): void;
abstract forward(): void;
abstract back(): void;
abstract onPopState(fn: (_: any) => any): void;
abstract getBaseHref(): string;
}

export function normalizeQueryParams(params: string): string {
return (params.length > 0 && params.substring(0, 1) != '?') ? ('?' + params) : params;
}
8 changes: 5 additions & 3 deletions modules/angular2/src/router/path_location_strategy.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {DOM} from 'angular2/src/core/dom/dom_adapter';
import {Injectable} from 'angular2/angular2';
import {EventListener, History, Location} from 'angular2/src/core/facade/browser';
import {LocationStrategy} from './location_strategy';
import {LocationStrategy, normalizeQueryParams} from './location_strategy';

/**
* `PathLocationStrategy` is a {@link LocationStrategy} used to configure the
Expand Down Expand Up @@ -67,9 +67,11 @@ export class PathLocationStrategy extends LocationStrategy {

getBaseHref(): string { return this._baseHref; }

path(): string { return this._location.pathname; }
path(): string { return this._location.pathname + normalizeQueryParams(this._location.search); }

pushState(state: any, title: string, url: string) { this._history.pushState(state, title, url); }
pushState(state: any, title: string, url: string, queryParams: string) {
this._history.pushState(state, title, (url + normalizeQueryParams(queryParams)));
}

forward(): void { this._history.forward(); }

Expand Down
17 changes: 12 additions & 5 deletions modules/angular2/src/router/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,13 @@ import {
} from 'angular2/src/core/facade/lang';
import {BaseException, WrappedException} from 'angular2/src/core/facade/exceptions';
import {RouteRegistry} from './route_registry';
import {ComponentInstruction, Instruction, stringifyInstruction} from './instruction';
import {
ComponentInstruction,
Instruction,
stringifyInstruction,
stringifyInstructionPath,
stringifyInstructionQuery
} from './instruction';
import {RouterOutlet} from './router_outlet';
import {Location} from './location';
import {getCanActivateHook} from './route_lifecycle_reflector';
Expand Down Expand Up @@ -472,13 +478,14 @@ export class RootRouter extends Router {
}

commit(instruction: Instruction, _skipLocationChange: boolean = false): Promise<any> {
var emitUrl = stringifyInstruction(instruction);
if (emitUrl.length > 0) {
emitUrl = '/' + emitUrl;
var emitPath = stringifyInstructionPath(instruction);
var emitQuery = stringifyInstructionQuery(instruction);
if (emitPath.length > 0) {
emitPath = '/' + emitPath;
}
var promise = super.commit(instruction);
if (!_skipLocationChange) {
promise = promise.then((_) => { this._location.go(emitUrl); });
promise = promise.then((_) => { this._location.go(emitPath, emitQuery); });
}
return promise;
}
Expand Down
4 changes: 2 additions & 2 deletions modules/angular2/src/router/url_parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,10 @@ export function pathSegmentsToUrl(pathSegments: string[]): Url {
return url;
}

var SEGMENT_RE = RegExpWrapper.create('^[^\\/\\(\\)\\?;=&]+');
var SEGMENT_RE = RegExpWrapper.create('^[^\\/\\(\\)\\?;=&#]+');
function matchUrlSegment(str: string): string {
var match = RegExpWrapper.firstMatch(SEGMENT_RE, str);
return isPresent(match) ? match[0] : null;
return isPresent(match) ? match[0] : '';
}

export class UrlParser {
Expand Down

0 comments on commit 8bc40d3

Please sign in to comment.