Skip to content

Commit

Permalink
feat($state): support URLs with #fragments
Browse files Browse the repository at this point in the history
Allow setting the `#` param to update the location fragment; e.g.: `ui-sref="page({name: 'name', '#': 'frag'})"` or `$state.go('page', {name: 'name', '#': 'frag'})`
  • Loading branch information
Murray Smith committed Apr 8, 2015
1 parent c231865 commit 3da0a17
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 2 deletions.
8 changes: 7 additions & 1 deletion src/state.js
Original file line number Diff line number Diff line change
Expand Up @@ -968,6 +968,9 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) {
var from = $state.$current, fromParams = $state.params, fromPath = from.path;
var evt, toState = findState(to, options.relative);

// Store the hash param for later (since it will be stripped out by various methods)
var hash = toParams['#'];

if (!isDefined(toState)) {
var redirect = { to: to, toParams: toParams, options: options };
var redirectResult = handleRedirect(redirect, from.self, fromParams, options);
Expand Down Expand Up @@ -1117,6 +1120,9 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) {
}
}

// Re-add the saved hash before we start returning things
if (hash) toParams['#'] = hash;

// Run it again, to catch any transitions in callbacks
if ($state.transition !== transition) return TransitionSuperseded;

Expand Down Expand Up @@ -1342,7 +1348,7 @@ function $StateProvider( $urlRouterProvider, $urlMatcherFactory) {
if (!nav || nav.url === undefined || nav.url === null) {
return null;
}
return $urlRouter.href(nav.url, filterByKeys(state.params.$$keys(), params || {}), {
return $urlRouter.href(nav.url, filterByKeys(state.params.$$keys().concat('#'), params || {}), {
absolute: options.absolute
});
};
Expand Down
15 changes: 14 additions & 1 deletion src/urlRouter.js
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,14 @@ function $UrlRouterProvider( $locationProvider, $urlMatcherFactory) {
},

push: function(urlMatcher, params, options) {
$location.url(urlMatcher.format(params || {}));
var url = urlMatcher.format(params || {});

// Handle the special hash param, if needed
if (url !== null && params && params['#']) {
url += '#' + params['#'];
}

$location.url(url);
lastPushedUrl = options && options.$$avoidResync ? $location.url() : undefined;
if (options && options.replace) $location.replace();
},
Expand Down Expand Up @@ -395,6 +402,12 @@ function $UrlRouterProvider( $locationProvider, $urlMatcherFactory) {
if (!isHtml5 && url !== null) {
url = "#" + $locationProvider.hashPrefix() + url;
}

// Handle special hash param, if needed
if (url !== null && params && params['#']) {
url += '#' + params['#'];
}

url = appendBasePath(url, isHtml5, options.absolute);

if (!options.absolute || !url) {
Expand Down
18 changes: 18 additions & 0 deletions test/stateSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,24 @@ describe('state', function () {
$state.transitionTo('dynamicController', { type: "Acme" });
$q.flush();
expect(ctrlName).toEqual("AcmeFooController");
}));+

it('updates the location #fragment, if specified', inject(function ($state, $q, $location) {
// html5mode disabled
locationProvider.html5Mode(false);
expect(locationProvider.html5Mode()).toBe(false);
$state.transitionTo('home.item', {id: 'world', '#': 'frag'});
$q.flush();
expect($location.url()).toBe('/front/world#frag');
expect($location.hash()).toBe('frag');

// html5mode enabled
locationProvider.html5Mode(true);
expect(locationProvider.html5Mode()).toBe(true);
$state.transitionTo('home.item', {id: 'world', '#': 'frag'});
$q.flush();
expect($location.url()).toBe('/front/world#frag');
expect($location.hash()).toBe('frag');
}));
});

Expand Down
28 changes: 28 additions & 0 deletions test/urlRouterSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,22 @@ describe("UrlRouter", function () {
expect($location.url).toHaveBeenCalledWith("/hello/");
}));

it('can push location changes that include a #fragment', inject(function($urlRouter, $location) {
// html5mode disabled
$lp.html5Mode(false);
expect($lp.html5Mode()).toBe(false);
$urlRouter.push(new UrlMatcher('/hello/:name'), {name: 'world', '#': 'frag'});
expect($location.url()).toBe('/hello/world#frag');
expect($location.hash()).toBe('frag');

// html5mode enabled
$lp.html5Mode(true);
expect($lp.html5Mode()).toBe(true);
$urlRouter.push(new UrlMatcher('/hello/:name'), {name: 'world', '#': 'frag'});
expect($location.url()).toBe('/hello/world#frag');
expect($location.hash()).toBe('frag');
}));

it('can read and sync a copy of location URL', inject(function($urlRouter, $location) {
$location.url('/old');

Expand Down Expand Up @@ -208,6 +224,18 @@ describe("UrlRouter", function () {

expect($urlRouter.href(new UrlMatcher('/hello'))).toBe('#/hello');
}));

it('should return URLs with #fragments', inject(function($urlRouter) {
// html5mode disabled
$lp.html5Mode(false);
expect($lp.html5Mode()).toBe(false);
expect($urlRouter.href(new UrlMatcher('/hello/:name'), {name: 'world', '#': 'frag'})).toBe('#/hello/world#frag');

// html5mode enabled
$lp.html5Mode(true);
expect($lp.html5Mode()).toBe(true);
expect($urlRouter.href(new UrlMatcher('/hello/:name'), {name: 'world', '#': 'frag'})).toBe('/hello/world#frag');
}));
});
});

Expand Down

0 comments on commit 3da0a17

Please sign in to comment.