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
This commit is contained in:
parent
b571baab68
commit
ac38812809
|
@ -9,10 +9,10 @@ export {RouterOutlet} from './src/router/router_outlet';
|
|||
export {RouterLink} from './src/router/router_link';
|
||||
export {RouteParams, RouteData} from './src/router/instruction';
|
||||
export {RouteRegistry} from './src/router/route_registry';
|
||||
export {LocationStrategy} from './src/router/location_strategy';
|
||||
export {LocationStrategy, APP_BASE_HREF} from './src/router/location_strategy';
|
||||
export {HashLocationStrategy} from './src/router/hash_location_strategy';
|
||||
export {PathLocationStrategy} from './src/router/path_location_strategy';
|
||||
export {Location, APP_BASE_HREF} from './src/router/location';
|
||||
export {Location} from './src/router/location';
|
||||
export * from './src/router/route_config_decorator';
|
||||
export * from './src/router/route_definition';
|
||||
export {OnActivate, OnDeactivate, OnReuse, CanDeactivate, CanReuse} from './src/router/interfaces';
|
||||
|
|
|
@ -18,7 +18,12 @@ export class MockLocationStrategy extends LocationStrategy {
|
|||
|
||||
path(): string { return this.internalPath; }
|
||||
|
||||
prepareExternalUrl(internal: string): string { return internal; }
|
||||
prepareExternalUrl(internal: string): string {
|
||||
if (internal.startsWith('/') && this.internalBaseHref.endsWith('/')) {
|
||||
return this.internalBaseHref + internal.substring(1);
|
||||
}
|
||||
return this.internalBaseHref + internal;
|
||||
}
|
||||
|
||||
simulateUrlPop(pathname: string): void {
|
||||
ObservableWrapper.callNext(this._subject, {'url': pathname});
|
||||
|
@ -29,7 +34,9 @@ export class MockLocationStrategy extends LocationStrategy {
|
|||
|
||||
var url = path + (query.length > 0 ? ('?' + query) : '');
|
||||
this.internalPath = url;
|
||||
this.urlChanges.push(url);
|
||||
|
||||
var external = this.prepareExternalUrl(url);
|
||||
this.urlChanges.push(external);
|
||||
}
|
||||
|
||||
onPopState(fn: (value: any) => void): void { ObservableWrapper.subscribe(this._subject, fn); }
|
||||
|
|
|
@ -1,40 +1,6 @@
|
|||
import {LocationStrategy} from './location_strategy';
|
||||
import {StringWrapper, isPresent, CONST_EXPR} from 'angular2/src/facade/lang';
|
||||
import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
|
||||
import {isBlank} from 'angular2/src/facade/lang';
|
||||
import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
|
||||
import {OpaqueToken, Injectable, Optional, Inject} from 'angular2/angular2';
|
||||
|
||||
/**
|
||||
* The `APP_BASE_HREF` token represents the base href to be used with the
|
||||
* {@link PathLocationStrategy}.
|
||||
*
|
||||
* If you're using {@link PathLocationStrategy}, you must provide a provider to a string
|
||||
* representing the URL prefix that should be preserved when generating and recognizing
|
||||
* URLs.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* ```
|
||||
* import {Component} from 'angular2/angular2';
|
||||
* import {ROUTER_DIRECTIVES, ROUTER_PROVIDERS, RouteConfig} from 'angular2/router';
|
||||
*
|
||||
* @Component({directives: [ROUTER_DIRECTIVES]})
|
||||
* @RouteConfig([
|
||||
* {...},
|
||||
* ])
|
||||
* class AppCmp {
|
||||
* // ...
|
||||
* }
|
||||
*
|
||||
* bootstrap(AppCmp, [
|
||||
* ROUTER_PROVIDERS,
|
||||
* PathLocationStrategy,
|
||||
* provide(APP_BASE_HREF, {useValue: '/my/app'})
|
||||
* ]);
|
||||
* ```
|
||||
*/
|
||||
export const APP_BASE_HREF: OpaqueToken = CONST_EXPR(new OpaqueToken('appBaseHref'));
|
||||
import {Injectable, Inject} from 'angular2/angular2';
|
||||
|
||||
/**
|
||||
* `Location` is a service that applications can use to interact with a browser's URL.
|
||||
|
@ -83,15 +49,8 @@ export class Location {
|
|||
/** @internal */
|
||||
_baseHref: string;
|
||||
|
||||
constructor(public platformStrategy: LocationStrategy,
|
||||
@Optional() @Inject(APP_BASE_HREF) href?: string) {
|
||||
var browserBaseHref = isPresent(href) ? href : this.platformStrategy.getBaseHref();
|
||||
|
||||
if (isBlank(browserBaseHref)) {
|
||||
throw new BaseException(
|
||||
`No base href set. Either provide a provider for the APP_BASE_HREF token or add a base element to the document.`);
|
||||
}
|
||||
|
||||
constructor(public platformStrategy: LocationStrategy) {
|
||||
var browserBaseHref = this.platformStrategy.getBaseHref();
|
||||
this._baseHref = stripTrailingSlash(stripIndexHtml(browserBaseHref));
|
||||
this.platformStrategy.onPopState(
|
||||
(_) => { ObservableWrapper.callNext(this._subject, {'url': this.path(), 'pop': true}); });
|
||||
|
@ -117,11 +76,10 @@ export class Location {
|
|||
* used, or the `APP_BASE_HREF` if the `PathLocationStrategy` is in use.
|
||||
*/
|
||||
prepareExternalUrl(url: string): string {
|
||||
if (!url.startsWith('/')) {
|
||||
if (url.length > 0 && !url.startsWith('/')) {
|
||||
url = '/' + url;
|
||||
}
|
||||
return this.platformStrategy.prepareExternalUrl(
|
||||
stripTrailingSlash(_addBaseHref(this._baseHref, url)));
|
||||
return this.platformStrategy.prepareExternalUrl(url);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -158,13 +116,6 @@ function _stripBaseHref(baseHref: string, url: string): string {
|
|||
return url;
|
||||
}
|
||||
|
||||
function _addBaseHref(baseHref: string, url: string): string {
|
||||
if (!url.startsWith(baseHref)) {
|
||||
return baseHref + url;
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
function stripIndexHtml(url: string): string {
|
||||
if (/\/index.html$/g.test(url)) {
|
||||
// '/index.html'.length == 11
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
import {CONST_EXPR} from 'angular2/src/facade/lang';
|
||||
import {OpaqueToken} from 'angular2/angular2';
|
||||
|
||||
/**
|
||||
* `LocationStrategy` is responsible for representing and reading route state
|
||||
* from the the browser's URL. Angular provides two strategies:
|
||||
|
@ -24,6 +27,38 @@ export abstract class LocationStrategy {
|
|||
abstract getBaseHref(): string;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The `APP_BASE_HREF` token represents the base href to be used with the
|
||||
* {@link PathLocationStrategy}.
|
||||
*
|
||||
* If you're using {@link PathLocationStrategy}, you must provide a provider to a string
|
||||
* representing the URL prefix that should be preserved when generating and recognizing
|
||||
* URLs.
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* ```
|
||||
* import {Component} from 'angular2/angular2';
|
||||
* import {ROUTER_DIRECTIVES, ROUTER_PROVIDERS, RouteConfig} from 'angular2/router';
|
||||
*
|
||||
* @Component({directives: [ROUTER_DIRECTIVES]})
|
||||
* @RouteConfig([
|
||||
* {...},
|
||||
* ])
|
||||
* class AppCmp {
|
||||
* // ...
|
||||
* }
|
||||
*
|
||||
* bootstrap(AppCmp, [
|
||||
* ROUTER_PROVIDERS,
|
||||
* PathLocationStrategy,
|
||||
* provide(APP_BASE_HREF, {useValue: '/my/app'})
|
||||
* ]);
|
||||
* ```
|
||||
*/
|
||||
export const APP_BASE_HREF: OpaqueToken = CONST_EXPR(new OpaqueToken('appBaseHref'));
|
||||
|
||||
export function normalizeQueryParams(params: string): string {
|
||||
return (params.length > 0 && params.substring(0, 1) != '?') ? ('?' + params) : params;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import {DOM} from 'angular2/src/core/dom/dom_adapter';
|
||||
import {Injectable} from 'angular2/angular2';
|
||||
import {Injectable, Inject} from 'angular2/angular2';
|
||||
import {EventListener, History, Location} from 'angular2/src/facade/browser';
|
||||
import {LocationStrategy, normalizeQueryParams} from './location_strategy';
|
||||
import {isBlank} from 'angular2/src/facade/lang';
|
||||
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||
import {LocationStrategy, APP_BASE_HREF, normalizeQueryParams} from './location_strategy';
|
||||
|
||||
/**
|
||||
* `PathLocationStrategy` is a {@link LocationStrategy} used to configure the
|
||||
|
@ -54,11 +56,21 @@ export class PathLocationStrategy extends LocationStrategy {
|
|||
private _history: History;
|
||||
private _baseHref: string;
|
||||
|
||||
constructor() {
|
||||
constructor(@Inject(APP_BASE_HREF) href?: string) {
|
||||
super();
|
||||
|
||||
if (isBlank(href)) {
|
||||
href = DOM.getBaseHref();
|
||||
}
|
||||
|
||||
if (isBlank(href)) {
|
||||
throw new BaseException(
|
||||
`No base href set. Please provide a value for the APP_BASE_HREF token or add a base element to the document.`);
|
||||
}
|
||||
|
||||
this._location = DOM.getLocation();
|
||||
this._history = DOM.getHistory();
|
||||
this._baseHref = DOM.getBaseHref();
|
||||
this._baseHref = href;
|
||||
}
|
||||
|
||||
onPopState(fn: EventListener): void {
|
||||
|
@ -68,12 +80,18 @@ export class PathLocationStrategy extends LocationStrategy {
|
|||
|
||||
getBaseHref(): string { return this._baseHref; }
|
||||
|
||||
prepareExternalUrl(internal: string): string { return this._baseHref + internal; }
|
||||
prepareExternalUrl(internal: string): string {
|
||||
if (internal.startsWith('/') && this._baseHref.endsWith('/')) {
|
||||
return this._baseHref + internal.substring(1);
|
||||
}
|
||||
return this._baseHref + internal;
|
||||
}
|
||||
|
||||
path(): string { return this._location.pathname + normalizeQueryParams(this._location.search); }
|
||||
|
||||
pushState(state: any, title: string, url: string, queryParams: string) {
|
||||
this._history.pushState(state, title, (url + normalizeQueryParams(queryParams)));
|
||||
var externalUrl = this.prepareExternalUrl(url + normalizeQueryParams(queryParams));
|
||||
this._history.pushState(state, title, externalUrl);
|
||||
}
|
||||
|
||||
forward(): void { this._history.forward(); }
|
||||
|
|
|
@ -14,8 +14,9 @@ import {
|
|||
|
||||
import {Injector, provide} from 'angular2/core';
|
||||
import {CONST_EXPR} from 'angular2/src/facade/lang';
|
||||
import {Location, APP_BASE_HREF} from 'angular2/src/router/location';
|
||||
import {LocationStrategy} from 'angular2/src/router/location_strategy';
|
||||
|
||||
import {Location} from 'angular2/src/router/location';
|
||||
import {LocationStrategy, APP_BASE_HREF} from 'angular2/src/router/location_strategy';
|
||||
import {MockLocationStrategy} from 'angular2/src/mock/mock_location_strategy';
|
||||
|
||||
export function main() {
|
||||
|
@ -54,14 +55,6 @@ export function main() {
|
|||
})
|
||||
}));
|
||||
|
||||
it('should throw when no base href is provided', () => {
|
||||
var locationStrategy = new MockLocationStrategy();
|
||||
locationStrategy.internalBaseHref = null;
|
||||
expect(() => new Location(locationStrategy))
|
||||
.toThrowError(
|
||||
`No base href set. Either provide a provider for the APP_BASE_HREF token or add a base element to the document.`);
|
||||
});
|
||||
|
||||
it('should revert to the previous path when a back() operation is executed', () => {
|
||||
var locationStrategy = new MockLocationStrategy();
|
||||
var location = new Location(locationStrategy);
|
||||
|
|
Loading…
Reference in New Issue