Skip to content

Commit ac38812

Browse files
committed
fix(router): apply APP_BASE_HREF when using PathLocationStrategy
Correctly initializes APP_BASE_HREF, and falls back to the `<base>` tag in the absence of an APP_BASE_HREF provider. Closes #5028
1 parent b571baa commit ac38812

6 files changed

Lines changed: 78 additions & 74 deletions

File tree

‎modules/angular2/router.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ export {RouterOutlet} from './src/router/router_outlet';
99
export {RouterLink} from './src/router/router_link';
1010
export {RouteParams, RouteData} from './src/router/instruction';
1111
export {RouteRegistry} from './src/router/route_registry';
12-
export {LocationStrategy} from './src/router/location_strategy';
12+
export {LocationStrategy, APP_BASE_HREF} from './src/router/location_strategy';
1313
export {HashLocationStrategy} from './src/router/hash_location_strategy';
1414
export {PathLocationStrategy} from './src/router/path_location_strategy';
15-
export {Location, APP_BASE_HREF} from './src/router/location';
15+
export {Location} from './src/router/location';
1616
export * from './src/router/route_config_decorator';
1717
export * from './src/router/route_definition';
1818
export {OnActivate, OnDeactivate, OnReuse, CanDeactivate, CanReuse} from './src/router/interfaces';

‎modules/angular2/src/mock/mock_location_strategy.ts‎

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,12 @@ export class MockLocationStrategy extends LocationStrategy {
1818

1919
path(): string { return this.internalPath; }
2020

21-
prepareExternalUrl(internal: string): string { return internal; }
21+
prepareExternalUrl(internal: string): string {
22+
if (internal.startsWith('/') && this.internalBaseHref.endsWith('/')) {
23+
return this.internalBaseHref + internal.substring(1);
24+
}
25+
return this.internalBaseHref + internal;
26+
}
2227

2328
simulateUrlPop(pathname: string): void {
2429
ObservableWrapper.callNext(this._subject, {'url': pathname});
@@ -29,7 +34,9 @@ export class MockLocationStrategy extends LocationStrategy {
2934

3035
var url = path + (query.length > 0 ? ('?' + query) : '');
3136
this.internalPath = url;
32-
this.urlChanges.push(url);
37+
38+
var external = this.prepareExternalUrl(url);
39+
this.urlChanges.push(external);
3340
}
3441

3542
onPopState(fn: (value: any) => void): void { ObservableWrapper.subscribe(this._subject, fn); }

‎modules/angular2/src/router/location.ts‎

Lines changed: 5 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,6 @@
11
import {LocationStrategy} from './location_strategy';
2-
import {StringWrapper, isPresent, CONST_EXPR} from 'angular2/src/facade/lang';
32
import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
4-
import {isBlank} from 'angular2/src/facade/lang';
5-
import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
6-
import {OpaqueToken, Injectable, Optional, Inject} from 'angular2/angular2';
7-
8-
/**
9-
* The `APP_BASE_HREF` token represents the base href to be used with the
10-
* {@link PathLocationStrategy}.
11-
*
12-
* If you're using {@link PathLocationStrategy}, you must provide a provider to a string
13-
* representing the URL prefix that should be preserved when generating and recognizing
14-
* URLs.
15-
*
16-
* ### Example
17-
*
18-
* ```
19-
* import {Component} from 'angular2/angular2';
20-
* import {ROUTER_DIRECTIVES, ROUTER_PROVIDERS, RouteConfig} from 'angular2/router';
21-
*
22-
* @Component({directives: [ROUTER_DIRECTIVES]})
23-
* @RouteConfig([
24-
* {...},
25-
* ])
26-
* class AppCmp {
27-
* // ...
28-
* }
29-
*
30-
* bootstrap(AppCmp, [
31-
* ROUTER_PROVIDERS,
32-
* PathLocationStrategy,
33-
* provide(APP_BASE_HREF, {useValue: '/my/app'})
34-
* ]);
35-
* ```
36-
*/
37-
export const APP_BASE_HREF: OpaqueToken = CONST_EXPR(new OpaqueToken('appBaseHref'));
3+
import {Injectable, Inject} from 'angular2/angular2';
384

395
/**
406
* `Location` is a service that applications can use to interact with a browser's URL.
@@ -83,15 +49,8 @@ export class Location {
8349
/** @internal */
8450
_baseHref: string;
8551

86-
constructor(public platformStrategy: LocationStrategy,
87-
@Optional() @Inject(APP_BASE_HREF) href?: string) {
88-
var browserBaseHref = isPresent(href) ? href : this.platformStrategy.getBaseHref();
89-
90-
if (isBlank(browserBaseHref)) {
91-
throw new BaseException(
92-
`No base href set. Either provide a provider for the APP_BASE_HREF token or add a base element to the document.`);
93-
}
94-
52+
constructor(public platformStrategy: LocationStrategy) {
53+
var browserBaseHref = this.platformStrategy.getBaseHref();
9554
this._baseHref = stripTrailingSlash(stripIndexHtml(browserBaseHref));
9655
this.platformStrategy.onPopState(
9756
(_) => { ObservableWrapper.callNext(this._subject, {'url': this.path(), 'pop': true}); });
@@ -117,11 +76,10 @@ export class Location {
11776
* used, or the `APP_BASE_HREF` if the `PathLocationStrategy` is in use.
11877
*/
11978
prepareExternalUrl(url: string): string {
120-
if (!url.startsWith('/')) {
79+
if (url.length > 0 && !url.startsWith('/')) {
12180
url = '/' + url;
12281
}
123-
return this.platformStrategy.prepareExternalUrl(
124-
stripTrailingSlash(_addBaseHref(this._baseHref, url)));
82+
return this.platformStrategy.prepareExternalUrl(url);
12583
}
12684

12785
/**
@@ -158,13 +116,6 @@ function _stripBaseHref(baseHref: string, url: string): string {
158116
return url;
159117
}
160118

161-
function _addBaseHref(baseHref: string, url: string): string {
162-
if (!url.startsWith(baseHref)) {
163-
return baseHref + url;
164-
}
165-
return url;
166-
}
167-
168119
function stripIndexHtml(url: string): string {
169120
if (/\/index.html$/g.test(url)) {
170121
// '/index.html'.length == 11

‎modules/angular2/src/router/location_strategy.ts‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import {CONST_EXPR} from 'angular2/src/facade/lang';
2+
import {OpaqueToken} from 'angular2/angular2';
3+
14
/**
25
* `LocationStrategy` is responsible for representing and reading route state
36
* from the the browser's URL. Angular provides two strategies:
@@ -24,6 +27,38 @@ export abstract class LocationStrategy {
2427
abstract getBaseHref(): string;
2528
}
2629

30+
31+
/**
32+
* The `APP_BASE_HREF` token represents the base href to be used with the
33+
* {@link PathLocationStrategy}.
34+
*
35+
* If you're using {@link PathLocationStrategy}, you must provide a provider to a string
36+
* representing the URL prefix that should be preserved when generating and recognizing
37+
* URLs.
38+
*
39+
* ### Example
40+
*
41+
* ```
42+
* import {Component} from 'angular2/angular2';
43+
* import {ROUTER_DIRECTIVES, ROUTER_PROVIDERS, RouteConfig} from 'angular2/router';
44+
*
45+
* @Component({directives: [ROUTER_DIRECTIVES]})
46+
* @RouteConfig([
47+
* {...},
48+
* ])
49+
* class AppCmp {
50+
* // ...
51+
* }
52+
*
53+
* bootstrap(AppCmp, [
54+
* ROUTER_PROVIDERS,
55+
* PathLocationStrategy,
56+
* provide(APP_BASE_HREF, {useValue: '/my/app'})
57+
* ]);
58+
* ```
59+
*/
60+
export const APP_BASE_HREF: OpaqueToken = CONST_EXPR(new OpaqueToken('appBaseHref'));
61+
2762
export function normalizeQueryParams(params: string): string {
2863
return (params.length > 0 && params.substring(0, 1) != '?') ? ('?' + params) : params;
2964
}

‎modules/angular2/src/router/path_location_strategy.ts‎

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import {DOM} from 'angular2/src/core/dom/dom_adapter';
2-
import {Injectable} from 'angular2/angular2';
2+
import {Injectable, Inject} from 'angular2/angular2';
33
import {EventListener, History, Location} from 'angular2/src/facade/browser';
4-
import {LocationStrategy, normalizeQueryParams} from './location_strategy';
4+
import {isBlank} from 'angular2/src/facade/lang';
5+
import {BaseException} from 'angular2/src/facade/exceptions';
6+
import {LocationStrategy, APP_BASE_HREF, normalizeQueryParams} from './location_strategy';
57

68
/**
79
* `PathLocationStrategy` is a {@link LocationStrategy} used to configure the
@@ -54,11 +56,21 @@ export class PathLocationStrategy extends LocationStrategy {
5456
private _history: History;
5557
private _baseHref: string;
5658

57-
constructor() {
59+
constructor(@Inject(APP_BASE_HREF) href?: string) {
5860
super();
61+
62+
if (isBlank(href)) {
63+
href = DOM.getBaseHref();
64+
}
65+
66+
if (isBlank(href)) {
67+
throw new BaseException(
68+
`No base href set. Please provide a value for the APP_BASE_HREF token or add a base element to the document.`);
69+
}
70+
5971
this._location = DOM.getLocation();
6072
this._history = DOM.getHistory();
61-
this._baseHref = DOM.getBaseHref();
73+
this._baseHref = href;
6274
}
6375

6476
onPopState(fn: EventListener): void {
@@ -68,12 +80,18 @@ export class PathLocationStrategy extends LocationStrategy {
6880

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

71-
prepareExternalUrl(internal: string): string { return this._baseHref + internal; }
83+
prepareExternalUrl(internal: string): string {
84+
if (internal.startsWith('/') && this._baseHref.endsWith('/')) {
85+
return this._baseHref + internal.substring(1);
86+
}
87+
return this._baseHref + internal;
88+
}
7289

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

7592
pushState(state: any, title: string, url: string, queryParams: string) {
76-
this._history.pushState(state, title, (url + normalizeQueryParams(queryParams)));
93+
var externalUrl = this.prepareExternalUrl(url + normalizeQueryParams(queryParams));
94+
this._history.pushState(state, title, externalUrl);
7795
}
7896

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

‎modules/angular2/test/router/location_spec.ts‎

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@ import {
1414

1515
import {Injector, provide} from 'angular2/core';
1616
import {CONST_EXPR} from 'angular2/src/facade/lang';
17-
import {Location, APP_BASE_HREF} from 'angular2/src/router/location';
18-
import {LocationStrategy} from 'angular2/src/router/location_strategy';
17+
18+
import {Location} from 'angular2/src/router/location';
19+
import {LocationStrategy, APP_BASE_HREF} from 'angular2/src/router/location_strategy';
1920
import {MockLocationStrategy} from 'angular2/src/mock/mock_location_strategy';
2021

2122
export function main() {
@@ -54,14 +55,6 @@ export function main() {
5455
})
5556
}));
5657

57-
it('should throw when no base href is provided', () => {
58-
var locationStrategy = new MockLocationStrategy();
59-
locationStrategy.internalBaseHref = null;
60-
expect(() => new Location(locationStrategy))
61-
.toThrowError(
62-
`No base href set. Either provide a provider for the APP_BASE_HREF token or add a base element to the document.`);
63-
});
64-
6558
it('should revert to the previous path when a back() operation is executed', () => {
6659
var locationStrategy = new MockLocationStrategy();
6760
var location = new Location(locationStrategy);

0 commit comments

Comments
 (0)