Skip to content

Commit 3da0a17

Browse files
author
Murray Smith
committed
feat($state): support URLs with #fragments
Allow setting the `#` param to update the location fragment; e.g.: `ui-sref="page({name: 'name', '#': 'frag'})"` or `$state.go('page', {name: 'name', '#': 'frag'})`
1 parent c231865 commit 3da0a17

File tree

4 files changed

+67
-2
lines changed

4 files changed

+67
-2
lines changed

src/state.js

+7-1
Original file line numberDiff line numberDiff line change
@@ -968,6 +968,9 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) {
968968
var from = $state.$current, fromParams = $state.params, fromPath = from.path;
969969
var evt, toState = findState(to, options.relative);
970970

971+
// Store the hash param for later (since it will be stripped out by various methods)
972+
var hash = toParams['#'];
973+
971974
if (!isDefined(toState)) {
972975
var redirect = { to: to, toParams: toParams, options: options };
973976
var redirectResult = handleRedirect(redirect, from.self, fromParams, options);
@@ -1117,6 +1120,9 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) {
11171120
}
11181121
}
11191122

1123+
// Re-add the saved hash before we start returning things
1124+
if (hash) toParams['#'] = hash;
1125+
11201126
// Run it again, to catch any transitions in callbacks
11211127
if ($state.transition !== transition) return TransitionSuperseded;
11221128

@@ -1342,7 +1348,7 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) {
13421348
if (!nav || nav.url === undefined || nav.url === null) {
13431349
return null;
13441350
}
1345-
return $urlRouter.href(nav.url, filterByKeys(state.params.$$keys(), params || {}), {
1351+
return $urlRouter.href(nav.url, filterByKeys(state.params.$$keys().concat('#'), params || {}), {
13461352
absolute: options.absolute
13471353
});
13481354
};

src/urlRouter.js

+14-1
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,14 @@ function $UrlRouterProvider( $locationProvider, $urlMatcherFactory) {
351351
},
352352

353353
push: function(urlMatcher, params, options) {
354-
$location.url(urlMatcher.format(params || {}));
354+
var url = urlMatcher.format(params || {});
355+
356+
// Handle the special hash param, if needed
357+
if (url !== null && params && params['#']) {
358+
url += '#' + params['#'];
359+
}
360+
361+
$location.url(url);
355362
lastPushedUrl = options && options.$$avoidResync ? $location.url() : undefined;
356363
if (options && options.replace) $location.replace();
357364
},
@@ -395,6 +402,12 @@ function $UrlRouterProvider( $locationProvider, $urlMatcherFactory) {
395402
if (!isHtml5 && url !== null) {
396403
url = "#" + $locationProvider.hashPrefix() + url;
397404
}
405+
406+
// Handle special hash param, if needed
407+
if (url !== null && params && params['#']) {
408+
url += '#' + params['#'];
409+
}
410+
398411
url = appendBasePath(url, isHtml5, options.absolute);
399412

400413
if (!options.absolute || !url) {

test/stateSpec.js

+18
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,24 @@ describe('state', function () {
488488
$state.transitionTo('dynamicController', { type: "Acme" });
489489
$q.flush();
490490
expect(ctrlName).toEqual("AcmeFooController");
491+
}));+
492+
493+
it('updates the location #fragment, if specified', inject(function ($state, $q, $location) {
494+
// html5mode disabled
495+
locationProvider.html5Mode(false);
496+
expect(locationProvider.html5Mode()).toBe(false);
497+
$state.transitionTo('home.item', {id: 'world', '#': 'frag'});
498+
$q.flush();
499+
expect($location.url()).toBe('/front/world#frag');
500+
expect($location.hash()).toBe('frag');
501+
502+
// html5mode enabled
503+
locationProvider.html5Mode(true);
504+
expect(locationProvider.html5Mode()).toBe(true);
505+
$state.transitionTo('home.item', {id: 'world', '#': 'frag'});
506+
$q.flush();
507+
expect($location.url()).toBe('/front/world#frag');
508+
expect($location.hash()).toBe('frag');
491509
}));
492510
});
493511

test/urlRouterSpec.js

+28
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,22 @@ describe("UrlRouter", function () {
173173
expect($location.url).toHaveBeenCalledWith("/hello/");
174174
}));
175175

176+
it('can push location changes that include a #fragment', inject(function($urlRouter, $location) {
177+
// html5mode disabled
178+
$lp.html5Mode(false);
179+
expect($lp.html5Mode()).toBe(false);
180+
$urlRouter.push(new UrlMatcher('/hello/:name'), {name: 'world', '#': 'frag'});
181+
expect($location.url()).toBe('/hello/world#frag');
182+
expect($location.hash()).toBe('frag');
183+
184+
// html5mode enabled
185+
$lp.html5Mode(true);
186+
expect($lp.html5Mode()).toBe(true);
187+
$urlRouter.push(new UrlMatcher('/hello/:name'), {name: 'world', '#': 'frag'});
188+
expect($location.url()).toBe('/hello/world#frag');
189+
expect($location.hash()).toBe('frag');
190+
}));
191+
176192
it('can read and sync a copy of location URL', inject(function($urlRouter, $location) {
177193
$location.url('/old');
178194

@@ -208,6 +224,18 @@ describe("UrlRouter", function () {
208224

209225
expect($urlRouter.href(new UrlMatcher('/hello'))).toBe('#/hello');
210226
}));
227+
228+
it('should return URLs with #fragments', inject(function($urlRouter) {
229+
// html5mode disabled
230+
$lp.html5Mode(false);
231+
expect($lp.html5Mode()).toBe(false);
232+
expect($urlRouter.href(new UrlMatcher('/hello/:name'), {name: 'world', '#': 'frag'})).toBe('#/hello/world#frag');
233+
234+
// html5mode enabled
235+
$lp.html5Mode(true);
236+
expect($lp.html5Mode()).toBe(true);
237+
expect($urlRouter.href(new UrlMatcher('/hello/:name'), {name: 'world', '#': 'frag'})).toBe('/hello/world#frag');
238+
}));
211239
});
212240
});
213241

0 commit comments

Comments
 (0)