diff --git a/docs/content/guide/dev_guide.services.$location.ngdoc b/docs/content/guide/dev_guide.services.$location.ngdoc index 788fe9a73000..cac8c04c573b 100644 --- a/docs/content/guide/dev_guide.services.$location.ngdoc +++ b/docs/content/guide/dev_guide.services.$location.ngdoc @@ -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: diff --git a/src/ng/location.js b/src/ng/location.js index 3196b1d57b7c..53cb16bd4cee 100644 --- a/src/ng/location.js +++ b/src/ng/location.js @@ -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 @@ -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; } }; @@ -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; }); diff --git a/test/ng/locationSpec.js b/test/ng/locationSpec.js index bf91c25023ca..b3a162aa9c23 100644 --- a/test/ng/locationSpec.js +++ b/test/ng/locationSpec.js @@ -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'); @@ -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'); @@ -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');