Skip to content

Commit c7e719b

Browse files
committed
feat($location): Location change notifications can now be skipped
This adds a new method `notify` to `$location` that allows updating the location without triggering `$locationChangeStart`/`$locationChangeSuccess` events. The method is chainable and must be called with a boolean parameter. Any falsy value will disable the notification procedure and will block any route update that may apply. The skip flag will be reset after any digest cycle, so it must be set again on any subsequent change if needed. Example: `$location.path('/client/2').replace().notify(false);` Closes angular#1699
1 parent ab755a2 commit c7e719b

File tree

3 files changed

+84
-5
lines changed

3 files changed

+84
-5
lines changed

docs/content/guide/dev_guide.services.$location.ngdoc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,9 @@ rather than an addition to the browser history. Once the browser is updated, the
141141
resets the flag set by `replace()` method and future mutations will create new history records,
142142
unless `replace()` is called again.
143143

144+
Also, you may want to update the location without broadcasting a notification in order to avoid
145+
triggering a route update. This can be accomplished calling `notify(false)` within the chain.
146+
144147
### Setters and character encoding
145148
You can pass special characters to `$location` service and it will encode them according to rules
146149
specified in {@link http://www.ietf.org/rfc/rfc3986.txt RFC 3986}. When you access the methods:

src/ng/location.js

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,12 @@ LocationUrl.prototype = {
224224
*/
225225
$$replace: false,
226226

227+
/**
228+
* Broadcast a location change event the next time it changes
229+
* @private
230+
*/
231+
$$notify: true,
232+
227233
/**
228234
* @ngdoc method
229235
* @name ng.$location#absUrl
@@ -395,6 +401,22 @@ LocationUrl.prototype = {
395401
replace: function() {
396402
this.$$replace = true;
397403
return this;
404+
},
405+
406+
/**
407+
* @ngdoc method
408+
* @name ng.$location#notify
409+
* @methodOf ng.$location
410+
*
411+
* @description
412+
* Allows to skip the location change broadcast event next time the location changes
413+
* when called with a false parameter
414+
*
415+
* @param {boolean=} notify Set to false to disable the location change event once
416+
*/
417+
notify: function(notify) {
418+
this.$$notify = notify;
419+
return this;
398420
}
399421
};
400422

@@ -592,20 +614,27 @@ function $LocationProvider(){
592614
$rootScope.$watch(function $locationWatch() {
593615
var oldUrl = $browser.url();
594616
var currentReplace = $location.$$replace;
617+
var currentNotify = $location.$$notify;
595618

596619
if (!changeCounter || oldUrl != $location.absUrl()) {
597620
changeCounter++;
598621
$rootScope.$evalAsync(function() {
599-
if ($rootScope.$broadcast('$locationChangeStart', $location.absUrl(), oldUrl).
600-
defaultPrevented) {
601-
$location.$$parse(oldUrl);
602-
} else {
622+
if (currentNotify == true) {
623+
if ($rootScope.$broadcast('$locationChangeStart', $location.absUrl(), oldUrl).
624+
defaultPrevented) {
625+
$location.$$parse(oldUrl);
626+
} else {
627+
$browser.url($location.absUrl(), currentReplace);
628+
afterLocationChange(oldUrl);
629+
}
630+
}
631+
else {
603632
$browser.url($location.absUrl(), currentReplace);
604-
afterLocationChange(oldUrl);
605633
}
606634
});
607635
}
608636
$location.$$replace = false;
637+
$location.$$notify = true;
609638

610639
return changeCounter;
611640
});

test/ng/locationSpec.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,15 @@ describe('$location', function() {
131131
});
132132

133133

134+
it('notify should set $$notify flag and return itself', function() {
135+
expect(url.$$notify).toBe(true);
136+
137+
url.notify(false);
138+
expect(url.$$notify).toBe(false);
139+
expect(url.notify(true)).toBe(url);
140+
});
141+
142+
134143
it('should parse new url', function() {
135144
url = new LocationUrl('http://host.com/base');
136145
expect(url.path()).toBe('/base');
@@ -451,6 +460,32 @@ describe('$location', function() {
451460
}));
452461

453462

463+
it('should skip location change notifications when notify is set to false', inject(
464+
function($log, $location, $browser, $rootScope) {
465+
466+
$rootScope.$on('$locationChangeStart', function(event, newUrl, oldUrl) {
467+
$log.info('before', newUrl, oldUrl, $browser.url());
468+
});
469+
$rootScope.$apply(); // initial $locationChangeStart
470+
expect($log.info.logs.shift()).
471+
toEqual(['before', 'http://new.com/a/b', 'http://new.com/a/b', 'http://new.com/a/b']);
472+
473+
// Setting notify to false shouldn't trigger $locationChangeStart
474+
$location.path('/any').notify(false);
475+
$rootScope.$apply();
476+
expect($log.info.logs).toEqual([]);
477+
expect($browser.url()).toEqual('http://new.com/a/b#!/any');
478+
479+
// Setting notify to true should trigger $locationChangeStart
480+
$location.path('/anyother').notify(true);
481+
$rootScope.$apply();
482+
expect($browser.url()).toEqual('http://new.com/a/b#!/anyother');
483+
expect($log.info.logs.shift()).
484+
toEqual(['before', 'http://new.com/a/b#!/anyother', 'http://new.com/a/b#!/any', 'http://new.com/a/b#!/any']);
485+
})
486+
);
487+
488+
454489
it('should always reset replace flag after running watch', inject(function($rootScope, $location) {
455490
// init watches
456491
$location.url('/initUrl');
@@ -473,6 +508,18 @@ describe('$location', function() {
473508
}));
474509

475510

511+
it('should always reset notify flag after running watch', inject(function($rootScope, $location) {
512+
// flag gets reset after digest
513+
$location.url('/newUrl').notify(false);
514+
$rootScope.$apply();
515+
expect($location.$$notify).toBe(true);
516+
517+
// even if no changes on location are stated
518+
$location.notify(false);
519+
$rootScope.$apply();
520+
expect($location.$$notify).toBe(true);
521+
}));
522+
476523
it('should update the browser if changed from within a watcher', inject(function($rootScope, $location, $browser) {
477524
$rootScope.$watch(function() { return true; }, function() {
478525
$location.path('/changed');

0 commit comments

Comments
 (0)