fix(router): strip base href from URLs when navigating
This commit is contained in:
parent
84dc6ae76b
commit
853d1de6ec
|
@ -13,6 +13,7 @@ export {RouteParams} from './src/router/instruction';
|
||||||
export * from './src/router/route_config_annotation';
|
export * from './src/router/route_config_annotation';
|
||||||
export * from './src/router/route_config_decorator';
|
export * from './src/router/route_config_decorator';
|
||||||
|
|
||||||
|
import {BrowserLocation} from './src/router/browser_location';
|
||||||
import {Router, RootRouter} from './src/router/router';
|
import {Router, RootRouter} from './src/router/router';
|
||||||
import {RouteRegistry} from './src/router/route_registry';
|
import {RouteRegistry} from './src/router/route_registry';
|
||||||
import {Pipeline} from './src/router/pipeline';
|
import {Pipeline} from './src/router/pipeline';
|
||||||
|
@ -23,6 +24,7 @@ import {bind} from './di';
|
||||||
export var routerInjectables:List = [
|
export var routerInjectables:List = [
|
||||||
RouteRegistry,
|
RouteRegistry,
|
||||||
Pipeline,
|
Pipeline,
|
||||||
|
BrowserLocation,
|
||||||
Location,
|
Location,
|
||||||
bind(Router).toFactory((registry, pipeline, location, meta) => {
|
bind(Router).toFactory((registry, pipeline, location, meta) => {
|
||||||
return new RootRouter(registry, pipeline, location, meta.type);
|
return new RootRouter(registry, pipeline, location, meta.type);
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||||
|
|
||||||
|
export class BrowserLocation {
|
||||||
|
_location;
|
||||||
|
_history;
|
||||||
|
_baseHref:string;
|
||||||
|
constructor() {
|
||||||
|
this._location = DOM.getLocation();
|
||||||
|
this._history = DOM.getHistory();
|
||||||
|
this._baseHref = DOM.getBaseHref();
|
||||||
|
}
|
||||||
|
|
||||||
|
onPopState(fn) {
|
||||||
|
DOM.getGlobalEventTarget('window').addEventListener('popstate', fn, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
getBaseHref() {
|
||||||
|
return this._baseHref;
|
||||||
|
}
|
||||||
|
|
||||||
|
path() {
|
||||||
|
return this._location.pathname;
|
||||||
|
}
|
||||||
|
|
||||||
|
pushState(state:any, title:string, url:string) {
|
||||||
|
this._history.pushState(state, title, url);
|
||||||
|
}
|
||||||
|
|
||||||
|
forward() {
|
||||||
|
this._history.forward();
|
||||||
|
}
|
||||||
|
|
||||||
|
back() {
|
||||||
|
this._history.back();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,40 +1,63 @@
|
||||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
import {BrowserLocation} from './browser_location';
|
||||||
|
import {StringWrapper} from 'angular2/src/facade/lang';
|
||||||
import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
|
import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
|
||||||
|
|
||||||
export class Location {
|
export class Location {
|
||||||
_location;
|
|
||||||
_subject:EventEmitter;
|
_subject:EventEmitter;
|
||||||
_history;
|
_browserLocation:BrowserLocation;
|
||||||
constructor() {
|
_baseHref:string;
|
||||||
|
constructor(browserLocation:BrowserLocation) {
|
||||||
this._subject = new EventEmitter();
|
this._subject = new EventEmitter();
|
||||||
this._location = DOM.getLocation();
|
this._browserLocation = browserLocation;
|
||||||
this._history = DOM.getHistory();
|
this._baseHref = stripIndexHtml(this._browserLocation.getBaseHref());
|
||||||
DOM.getGlobalEventTarget('window').addEventListener('popstate', (_) => this._onPopState(_), false);
|
this._browserLocation.onPopState((_) => this._onPopState(_));
|
||||||
}
|
}
|
||||||
|
|
||||||
_onPopState(_) {
|
_onPopState(_) {
|
||||||
ObservableWrapper.callNext(this._subject, {
|
ObservableWrapper.callNext(this._subject, {
|
||||||
'url': this._location.pathname
|
'url': this.path()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
path() {
|
path() {
|
||||||
return this._location.pathname;
|
return this.normalize(this._browserLocation.path());
|
||||||
|
}
|
||||||
|
|
||||||
|
normalize(url) {
|
||||||
|
return this._stripBaseHref(stripIndexHtml(url));
|
||||||
|
}
|
||||||
|
|
||||||
|
_stripBaseHref(url) {
|
||||||
|
if (this._baseHref.length > 0 && StringWrapper.startsWith(url, this._baseHref)) {
|
||||||
|
return StringWrapper.substring(url, this._baseHref.length);
|
||||||
|
}
|
||||||
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
go(url:string) {
|
go(url:string) {
|
||||||
this._history.pushState(null, null, url);
|
url = this._stripBaseHref(url);
|
||||||
|
this._browserLocation.pushState(null, null, url);
|
||||||
}
|
}
|
||||||
|
|
||||||
forward() {
|
forward() {
|
||||||
this._history.forward();
|
this._browserLocation.forward();
|
||||||
}
|
}
|
||||||
|
|
||||||
back() {
|
back() {
|
||||||
this._history.back()
|
this._browserLocation.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
subscribe(onNext, onThrow = null, onReturn = null) {
|
subscribe(onNext, onThrow = null, onReturn = null) {
|
||||||
ObservableWrapper.subscribe(this._subject, onNext, onThrow, onReturn);
|
ObservableWrapper.subscribe(this._subject, onNext, onThrow, onReturn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function stripIndexHtml(url) {
|
||||||
|
// '/index.html'.length == 11
|
||||||
|
if (url.length > 10 && StringWrapper.substring(url, url.length - 11) == '/index.html') {
|
||||||
|
return StringWrapper.substring(url, 0, url.length - 11);
|
||||||
|
}
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,85 @@
|
||||||
|
import {
|
||||||
|
AsyncTestCompleter,
|
||||||
|
describe,
|
||||||
|
proxy,
|
||||||
|
it, iit,
|
||||||
|
ddescribe, expect,
|
||||||
|
inject, beforeEach, beforeEachBindings,
|
||||||
|
SpyObject} from 'angular2/test_lib';
|
||||||
|
import {IMPLEMENTS} from 'angular2/src/facade/lang';
|
||||||
|
import {EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
|
||||||
|
|
||||||
|
import {BrowserLocation} from 'angular2/src/router/browser_location';
|
||||||
|
import {Location} from 'angular2/src/router/location';
|
||||||
|
|
||||||
|
export function main() {
|
||||||
|
|
||||||
|
describe('Location', () => {
|
||||||
|
|
||||||
|
var browserLocation, location;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
browserLocation = new DummyBrowserLocation();
|
||||||
|
browserLocation.spy('pushState');
|
||||||
|
browserLocation.baseHref = '/my/app';
|
||||||
|
location = new Location(browserLocation);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should normalize urls on navigate', () => {
|
||||||
|
location.go('/my/app/user/btford');
|
||||||
|
expect(browserLocation.spy('pushState')).toHaveBeenCalledWith(null, null, '/user/btford');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should remove index.html from base href', () => {
|
||||||
|
browserLocation.baseHref = '/my/app/index.html';
|
||||||
|
location = new Location(browserLocation);
|
||||||
|
location.go('/my/app/user/btford');
|
||||||
|
expect(browserLocation.spy('pushState')).toHaveBeenCalledWith(null, null, '/user/btford');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should normalize urls on popstate', inject([AsyncTestCompleter], (async) => {
|
||||||
|
browserLocation.simulatePopState('/my/app/user/btford');
|
||||||
|
location.subscribe((ev) => {
|
||||||
|
expect(ev['url']).toEqual('/user/btford');
|
||||||
|
async.done();
|
||||||
|
})
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should normalize location path', () => {
|
||||||
|
browserLocation.internalPath = '/my/app/user/btford';
|
||||||
|
expect(location.path()).toEqual('/user/btford');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@proxy
|
||||||
|
@IMPLEMENTS(BrowserLocation)
|
||||||
|
class DummyBrowserLocation extends SpyObject {
|
||||||
|
baseHref;
|
||||||
|
internalPath;
|
||||||
|
_subject:EventEmitter;
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.internalPath = '/';
|
||||||
|
this._subject = new EventEmitter();
|
||||||
|
}
|
||||||
|
|
||||||
|
simulatePopState(url) {
|
||||||
|
this.internalPath = url;
|
||||||
|
ObservableWrapper.callNext(this._subject, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
path() {
|
||||||
|
return this.internalPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
onPopState(fn) {
|
||||||
|
ObservableWrapper.subscribe(this._subject, fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
getBaseHref() {
|
||||||
|
return this.baseHref;
|
||||||
|
}
|
||||||
|
|
||||||
|
noSuchMethod(m){return super.noSuchMethod(m);}
|
||||||
|
}
|
Loading…
Reference in New Issue