Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

feat($location): Location change notifications can now be skipped #2398

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/content/guide/dev_guide.services.$location.ngdoc
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,9 @@ rather than an addition to the browser history. Once the browser is updated, the
resets the flag set by `replace()` method and future mutations will create new history records,
unless `replace()` is called again.

Also, you may want to update the location without broadcasting a notification in order to avoid
triggering a route update. This can be accomplished calling `notify(false)` within the chain.

### Setters and character encoding
You can pass special characters to `$location` service and it will encode them according to rules
specified in {@link http://www.ietf.org/rfc/rfc3986.txt RFC 3986}. When you access the methods:
Expand Down
39 changes: 34 additions & 5 deletions src/ng/location.js
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,12 @@ LocationUrl.prototype = {
*/
$$replace: false,

/**
* Broadcast a location change event the next time it changes
* @private
*/
$$notify: true,

/**
* @ngdoc method
* @name ng.$location#absUrl
Expand Down Expand Up @@ -395,6 +401,22 @@ LocationUrl.prototype = {
replace: function() {
this.$$replace = true;
return this;
},

/**
* @ngdoc method
* @name ng.$location#notify
* @methodOf ng.$location
*
* @description
* Allows to skip the location change broadcast event next time the location changes
* when called with a false parameter
*
* @param {boolean=} notify Set to false to disable the location change event once
*/
notify: function(notify) {
this.$$notify = notify;
return this;
}
};

Expand Down Expand Up @@ -592,20 +614,27 @@ function $LocationProvider(){
$rootScope.$watch(function $locationWatch() {
var oldUrl = $browser.url();
var currentReplace = $location.$$replace;
var currentNotify = $location.$$notify;

if (!changeCounter || oldUrl != $location.absUrl()) {
changeCounter++;
$rootScope.$evalAsync(function() {
if ($rootScope.$broadcast('$locationChangeStart', $location.absUrl(), oldUrl).
defaultPrevented) {
$location.$$parse(oldUrl);
} else {
if (currentNotify == true) {
if ($rootScope.$broadcast('$locationChangeStart', $location.absUrl(), oldUrl).
defaultPrevented) {
$location.$$parse(oldUrl);
} else {
$browser.url($location.absUrl(), currentReplace);
afterLocationChange(oldUrl);
}
}
else {
$browser.url($location.absUrl(), currentReplace);
afterLocationChange(oldUrl);
}
});
}
$location.$$replace = false;
$location.$$notify = true;

return changeCounter;
});
Expand Down
47 changes: 47 additions & 0 deletions test/ng/locationSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,15 @@ describe('$location', function() {
});


it('notify should set $$notify flag and return itself', function() {
expect(url.$$notify).toBe(true);

url.notify(false);
expect(url.$$notify).toBe(false);
expect(url.notify(true)).toBe(url);
});


it('should parse new url', function() {
url = new LocationUrl('http://host.com/base');
expect(url.path()).toBe('/base');
Expand Down Expand Up @@ -451,6 +460,32 @@ describe('$location', function() {
}));


it('should skip location change notifications when notify is set to false', inject(
function($log, $location, $browser, $rootScope) {

$rootScope.$on('$locationChangeStart', function(event, newUrl, oldUrl) {
$log.info('before', newUrl, oldUrl, $browser.url());
});
$rootScope.$apply(); // initial $locationChangeStart
expect($log.info.logs.shift()).
toEqual(['before', 'http://new.com/a/b', 'http://new.com/a/b', 'http://new.com/a/b']);

// Setting notify to false shouldn't trigger $locationChangeStart
$location.path('/any').notify(false);
$rootScope.$apply();
expect($log.info.logs).toEqual([]);
expect($browser.url()).toEqual('http://new.com/a/b#!/any');

// Setting notify to true should trigger $locationChangeStart
$location.path('/anyother').notify(true);
$rootScope.$apply();
expect($browser.url()).toEqual('http://new.com/a/b#!/anyother');
expect($log.info.logs.shift()).
toEqual(['before', 'http://new.com/a/b#!/anyother', 'http://new.com/a/b#!/any', 'http://new.com/a/b#!/any']);
})
);


it('should always reset replace flag after running watch', inject(function($rootScope, $location) {
// init watches
$location.url('/initUrl');
Expand All @@ -473,6 +508,18 @@ describe('$location', function() {
}));


it('should always reset notify flag after running watch', inject(function($rootScope, $location) {
// flag gets reset after digest
$location.url('/newUrl').notify(false);
$rootScope.$apply();
expect($location.$$notify).toBe(true);

// even if no changes on location are stated
$location.notify(false);
$rootScope.$apply();
expect($location.$$notify).toBe(true);
}));

it('should update the browser if changed from within a watcher', inject(function($rootScope, $location, $browser) {
$rootScope.$watch(function() { return true; }, function() {
$location.path('/changed');
Expand Down