Skip to content

Commit 73c665e

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 73c665e

File tree

2 files changed

+83
-1
lines changed

2 files changed

+83
-1
lines changed

src/ngRoute/route.js

+28-1
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
@@ -494,8 +514,15 @@ 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+
$rootScope.$broadcast('$routeChangeCancelled', next, last);
523+
return;
524+
}
525+
499526
$route.current = next;
500527
if (next) {
501528
if (next.redirectTo) {

test/ngRoute/routeSpec.js

+55
Original file line numberDiff line numberDiff line change
@@ -723,6 +723,61 @@ 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) {
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+
});
750+
});
751+
752+
753+
it('should not broadcast $routeChangeCancelled if force-reloading', function() {
754+
module(function($routeProvider) {
755+
$routeProvider.when('/r1', {
756+
templateUrl: 'foo.html'
757+
});
758+
});
759+
inject(function($route, $httpBackend, $location, $rootScope) {
760+
var success = jasmine.createSpy('$routeChangeSuccess'),
761+
error = jasmine.createSpy('$routeChangeError'),
762+
cancelled = jasmine.createSpy('$routeChangeCancelled');
763+
764+
$location.path('/r1');
765+
$rootScope.$digest();
766+
$httpBackend.flush();
767+
768+
$rootScope.$on('$routeChangeStart', function($event) { $event.preventDefault(); });
769+
$rootScope.$on('$routeChangeSuccess', success);
770+
$rootScope.$on('$routeChangeError', error);
771+
$rootScope.$on('$routeChangeCancelled', cancelled);
772+
773+
$route.reload();
774+
$rootScope.$digest();
775+
776+
expect(cancelled).not.toHaveBeenCalled();
777+
expect(error).not.toHaveBeenCalled();
778+
expect(success).toHaveBeenCalled();
779+
});
780+
});
726781
});
727782

728783

0 commit comments

Comments
 (0)