Skip to content

Commit cd426e5

Browse files
fix(state): When creating absolute hrefs in hashbang mode, include the location.pathname
Closes #3710
1 parent fe91bd3 commit cd426e5

File tree

3 files changed

+50
-14
lines changed

3 files changed

+50
-14
lines changed

src/locationServices.ts

+13-7
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,17 @@
44
*/ /** */
55
import { LocationConfig, LocationServices, UIRouter, ParamType, isDefined } from '@uirouter/core';
66
import { val, createProxyFunctions, removeFrom, isObject } from '@uirouter/core';
7-
import { ILocationService, ILocationProvider } from 'angular';
7+
import { ILocationService, ILocationProvider, IWindowService } from 'angular';
88

99
/**
1010
* Implements UI-Router LocationServices and LocationConfig using Angular 1's $location service
1111
*/
1212
export class Ng1LocationServices implements LocationConfig, LocationServices {
1313
private $locationProvider: ILocationProvider;
1414
private $location: ILocationService;
15-
private $sniffer;
15+
private $sniffer: any;
16+
private $browser: any;
17+
private $window: IWindowService;
1618

1719
path;
1820
search;
@@ -21,7 +23,8 @@ export class Ng1LocationServices implements LocationConfig, LocationServices {
2123
port;
2224
protocol;
2325
host;
24-
baseHref;
26+
27+
private _baseHref: string;
2528

2629
// .onChange() registry
2730
private _urlListeners: Function[] = [];
@@ -67,27 +70,30 @@ export class Ng1LocationServices implements LocationConfig, LocationServices {
6770
return html5Mode && this.$sniffer.history;
6871
}
6972

73+
baseHref() {
74+
return this._baseHref || (this._baseHref = this.$browser.baseHref() || this.$window.location.pathname);
75+
}
76+
7077
url(newUrl?: string, replace = false, state?) {
7178
if (isDefined(newUrl)) this.$location.url(newUrl);
7279
if (replace) this.$location.replace();
7380
if (state) this.$location.state(state);
7481
return this.$location.url();
7582
}
7683

77-
_runtimeServices($rootScope, $location: ILocationService, $sniffer, $browser) {
84+
_runtimeServices($rootScope, $location: ILocationService, $sniffer, $browser, $window: IWindowService) {
7885
this.$location = $location;
7986
this.$sniffer = $sniffer;
87+
this.$browser = $browser;
88+
this.$window = $window;
8089

8190
// Bind $locationChangeSuccess to the listeners registered in LocationService.onChange
8291
$rootScope.$on('$locationChangeSuccess', evt => this._urlListeners.forEach(fn => fn(evt)));
8392
const _loc = val($location);
84-
const _browser = val($browser);
8593

8694
// Bind these LocationService functions to $location
8795
createProxyFunctions(_loc, this, _loc, ['replace', 'path', 'search', 'hash']);
8896
// Bind these LocationConfig functions to $location
8997
createProxyFunctions(_loc, this, _loc, ['port', 'protocol', 'host']);
90-
// Bind these LocationConfig functions to $browser
91-
createProxyFunctions(_browser, this, _browser, ['baseHref']);
9298
}
9399
}

src/services.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -85,16 +85,17 @@ function $uiRouterProvider($locationProvider: ILocationProvider) {
8585
// backwards compat: also expose router instance as $uiRouterProvider.router
8686
router['router'] = router;
8787
router['$get'] = $get;
88-
$get.$inject = ['$location', '$browser', '$sniffer', '$rootScope', '$http', '$templateCache'];
88+
$get.$inject = ['$location', '$browser', '$window', '$sniffer', '$rootScope', '$http', '$templateCache'];
8989
function $get(
9090
$location: ILocationService,
9191
$browser: any,
92+
$window: any,
9293
$sniffer: any,
9394
$rootScope: ng.IScope,
9495
$http: IHttpService,
9596
$templateCache: ITemplateCacheService
9697
) {
97-
ng1LocationService._runtimeServices($rootScope, $location, $sniffer, $browser);
98+
ng1LocationService._runtimeServices($rootScope, $location, $sniffer, $browser, $window);
9899
delete router['router'];
99100
delete router['$get'];
100101
return router;

test/stateSpec.ts

+34-5
Original file line numberDiff line numberDiff line change
@@ -1211,11 +1211,40 @@ describe('state', function() {
12111211
expect($state.href('root', {}, { inherit: true })).toEqual('#/root?param1=1');
12121212
}));
12131213

1214-
it('generates absolute url when absolute is true', inject(function($state) {
1215-
expect($state.href('about.sidebar', null, { absolute: true })).toEqual('http://server/#/about');
1216-
locationProvider.html5Mode(true);
1217-
expect($state.href('about.sidebar', null, { absolute: true })).toEqual('http://server/about');
1218-
}));
1214+
describe('generates an absolute url', () => {
1215+
describe('when html5mode is false', () => {
1216+
it('and absolute is true', inject(function($state, $window) {
1217+
const pathname = $window.location.pathname;
1218+
$window.history.replaceState(null, '', '/');
1219+
expect($window.location.pathname).toBe('/');
1220+
expect($state.href('about.sidebar', null, { absolute: true })).toEqual('http://server/#/about');
1221+
$window.history.replaceState(null, '', pathname);
1222+
}));
1223+
1224+
it('and absolute is true and a base tag is present', inject(function($state, $window, $browser) {
1225+
spyOn($browser, 'baseHref').and.returnValue('/nested/path');
1226+
expect($state.href('about.sidebar', null, { absolute: true })).toEqual('http://server/nested/path#/about');
1227+
}));
1228+
1229+
it('and absolute is true and the app is served from a nested document root', inject(function(
1230+
$state,
1231+
$window,
1232+
$browser
1233+
) {
1234+
const pathname = $window.location.pathname;
1235+
$window.history.replaceState(null, 'nested path', '/nested/path');
1236+
expect($window.location.pathname).toBe('/nested/path');
1237+
spyOn($browser, 'baseHref').and.returnValue(null);
1238+
expect($state.href('about.sidebar', null, { absolute: true })).toEqual('http://server/nested/path#/about');
1239+
$window.history.replaceState(null, '', pathname);
1240+
}));
1241+
});
1242+
1243+
it('when html5Mode is true', inject(function($state) {
1244+
locationProvider.html5Mode(true);
1245+
expect($state.href('about.sidebar', null, { absolute: true })).toEqual('http://server/about');
1246+
}));
1247+
});
12191248

12201249
it('respects $locationProvider.hashPrefix()', inject(function($state) {
12211250
locationProvider.hashPrefix('!');

0 commit comments

Comments
 (0)