Skip to content

Commit 2317794

Browse files
committed
feat(ngRoute): cancel $routeChangeStart via $event.preventDefault()
This change allows ngRoute route changes to be cancelled using $event.preventDefault. This enables route changes to be aborted at the discretion of the application. Route changes may not be cancelled if forceReload is set to true. Closes angular#5581
1 parent 28fc80b commit 2317794

File tree

2 files changed

+86
-2
lines changed

2 files changed

+86
-2
lines changed

src/ngRoute/route.js

+30-2
Original file line numberDiff line numberDiff line change
@@ -375,11 +375,31 @@ function $RouteProvider(){
375375
* defined in `resolve` route property. Once all of the dependencies are resolved
376376
* `$routeChangeSuccess` is fired.
377377
*
378+
* The route change may be cancelled with angularEvent.preventDefault(), which will
379+
* in turn trigger a {@link ngRoute.$route#events_$routeChangeCancelled $routeChangeCancelled}
380+
* event.
381+
*
378382
* @param {Object} angularEvent Synthetic event object.
379383
* @param {Route} next Future route information.
380384
* @param {Route} current Current route information.
381385
*/
382386

387+
/**
388+
* @ngdoc event
389+
* @name ngRoute.$route#$routeChangeCancelled
390+
* @eventOf ngRoute.$route
391+
* @eventType broadcast on root scope
392+
* @description
393+
* Broadcasted after a route has been cancelled during
394+
* {@link ngRoute.$route#events_$routeChangeStart $routeChangStart}, using the
395+
* angularEvent.preventDefault() method. This method enables applications to prevent
396+
* a route change from occurring without any additional work.
397+
*
398+
* @param {Object} angularEvent Synthetic event object.
399+
* @param {Route} cancelled The cancelled route information.
400+
* @param {Route} current Current route information.
401+
*/
402+
383403
/**
384404
* @ngdoc event
385405
* @name ngRoute.$route#$routeChangeSuccess
@@ -483,7 +503,7 @@ function $RouteProvider(){
483503
return params;
484504
}
485505

486-
function updateRoute() {
506+
function updateRoute($event, newUrl, oldUrl) {
487507
var next = parseRoute(),
488508
last = $route.current;
489509

@@ -494,8 +514,16 @@ function $RouteProvider(){
494514
angular.copy(last.params, $routeParams);
495515
$rootScope.$broadcast('$routeUpdate', last);
496516
} else if (next || last) {
517+
var wasForceReload = forceReload;
497518
forceReload = false;
498-
$rootScope.$broadcast('$routeChangeStart', next, last);
519+
520+
if ($rootScope.$broadcast('$routeChangeStart', next, last).defaultPrevented &&
521+
!wasForceReload) {
522+
$location.$$parse($location.$$rewrite(oldUrl));
523+
$rootScope.$broadcast('$routeChangeCancelled', next, last);
524+
return;
525+
}
526+
499527
$route.current = next;
500528
if (next) {
501529
if (next.redirectTo) {

test/ngRoute/routeSpec.js

+56
Original file line numberDiff line numberDiff line change
@@ -723,6 +723,62 @@ describe('$route', function() {
723723
expect($exceptionHandler.errors).toEqual([myError]);
724724
});
725725
});
726+
727+
728+
it('should broadcast $routeChangeCancelled if $routeChangeSuccess.defaultPrevented', function() {
729+
module(function($routeProvider) {
730+
$routeProvider.when('/r1', {
731+
templateUrl: 'r1.html'
732+
});
733+
});
734+
inject(function($route, $httpBackend, $location, $rootScope, $browser) {
735+
var success = jasmine.createSpy('$routeChangeSuccess'),
736+
error = jasmine.createSpy('$routeChangeError'),
737+
cancelled = jasmine.createSpy('$routeChangeCancelled');
738+
$rootScope.$on('$routeChangeStart', function($event) { $event.preventDefault(); });
739+
$rootScope.$on('$routeChangeSuccess', success);
740+
$rootScope.$on('$routeChangeError', error);
741+
$rootScope.$on('$routeChangeCancelled', cancelled);
742+
743+
$location.path('/r1');
744+
$rootScope.$digest();
745+
746+
expect(success).not.toHaveBeenCalled();
747+
expect(error).not.toHaveBeenCalled();
748+
expect(cancelled).toHaveBeenCalled();
749+
expect($location.absUrl()).toBe('http://server/');
750+
});
751+
});
752+
753+
754+
it('should not broadcast $routeChangeCancelled if force-reloading', function() {
755+
module(function($routeProvider) {
756+
$routeProvider.when('/r1', {
757+
templateUrl: 'foo.html'
758+
});
759+
});
760+
inject(function($route, $httpBackend, $location, $rootScope) {
761+
var success = jasmine.createSpy('$routeChangeSuccess'),
762+
error = jasmine.createSpy('$routeChangeError'),
763+
cancelled = jasmine.createSpy('$routeChangeCancelled');
764+
765+
$location.path('/r1');
766+
$rootScope.$digest();
767+
$httpBackend.flush();
768+
769+
$rootScope.$on('$routeChangeStart', function($event) { $event.preventDefault(); });
770+
$rootScope.$on('$routeChangeSuccess', success);
771+
$rootScope.$on('$routeChangeError', error);
772+
$rootScope.$on('$routeChangeCancelled', cancelled);
773+
774+
$route.reload();
775+
$rootScope.$digest();
776+
777+
expect(cancelled).not.toHaveBeenCalled();
778+
expect(error).not.toHaveBeenCalled();
779+
expect(success).toHaveBeenCalled();
780+
});
781+
});
726782
});
727783

728784

0 commit comments

Comments
 (0)