From 2e6d9167d3afbfbca6427e53e012f94fb5fb8022 Mon Sep 17 00:00:00 2001 From: Roman Masek Date: Sun, 16 Mar 2014 12:42:10 +0100 Subject: [PATCH] fix(uiSref): cancel transition if preventDefault() has been called --- src/stateDirectives.js | 6 +++++- test/stateDirectivesSpec.js | 42 +++++++++++++++++++++++++++++++------ 2 files changed, 41 insertions(+), 7 deletions(-) diff --git a/src/stateDirectives.js b/src/stateDirectives.js index 4db3ef740..e56445fe3 100644 --- a/src/stateDirectives.js +++ b/src/stateDirectives.js @@ -128,10 +128,14 @@ function $StateRefDirective($state, $timeout) { var button = e.which || e.button; if ( !(button > 1 || e.ctrlKey || e.metaKey || e.shiftKey || element.attr('target')) ) { // HACK: This is to allow ng-clicks to be processed before the transition is initiated: - $timeout(function() { + var transition = $timeout(function() { $state.go(ref.state, params, options); }); e.preventDefault(); + + e.preventDefault = function() { + $timeout.cancel(transition); + }; } }); } diff --git a/test/stateDirectivesSpec.js b/test/stateDirectivesSpec.js index dbac84bf1..47f27c1cb 100644 --- a/test/stateDirectivesSpec.js +++ b/test/stateDirectivesSpec.js @@ -75,8 +75,9 @@ describe('uiStateRef', function() { }); describe('links', function() { + var timeoutFlush; - beforeEach(inject(function($rootScope, $compile) { + beforeEach(inject(function($rootScope, $compile, $timeout) { el = angular.element('Details'); scope = $rootScope; scope.contact = { id: 5 }; @@ -84,6 +85,15 @@ describe('uiStateRef', function() { $compile(el)(scope); scope.$digest(); + + timeoutFlush = function() { + try { + $timeout.flush(); + } catch (e) { + // Angular 1.0.8 throws 'No deferred tasks to be flushed' if there is nothing in queue. + // Behave as Angular >=1.1.5 and do nothing in such case. + } + } })); it('should generate the correct href', function() { @@ -107,18 +117,18 @@ describe('uiStateRef', function() { expect(el.attr('href')).toBe('#/contacts/3'); })); - it('should transition states when left-clicked', inject(function($state, $stateParams, $document, $q, $timeout) { + it('should transition states when left-clicked', inject(function($state, $stateParams, $document, $q) { expect($state.$current.name).toEqual(''); triggerClick(el); - $timeout.flush(); + timeoutFlush(); $q.flush(); expect($state.current.name).toEqual('contacts.item.detail'); expect($stateParams).toEqual({ id: "5" }); })); - it('should transition when given a click that contains no data (fake-click)', inject(function($state, $stateParams, $document, $q, $timeout) { + it('should transition when given a click that contains no data (fake-click)', inject(function($state, $stateParams, $document, $q) { expect($state.current.name).toEqual(''); triggerClick(el, { @@ -128,7 +138,7 @@ describe('uiStateRef', function() { altKey: undefined, button: undefined }); - $timeout.flush(); + timeoutFlush(); $q.flush(); expect($state.current.name).toEqual('contacts.item.detail'); @@ -139,7 +149,9 @@ describe('uiStateRef', function() { expect($state.$current.name).toEqual(''); triggerClick(el, { ctrlKey: true }); + timeoutFlush(); $q.flush(); + expect($state.current.name).toEqual(''); expect($stateParams).toEqual({ id: "5" }); })); @@ -148,6 +160,7 @@ describe('uiStateRef', function() { expect($state.$current.name).toEqual(''); triggerClick(el, { metaKey: true }); + timeoutFlush(); $q.flush(); expect($state.current.name).toEqual(''); @@ -158,6 +171,7 @@ describe('uiStateRef', function() { expect($state.$current.name).toEqual(''); triggerClick(el, { shiftKey: true }); + timeoutFlush(); $q.flush(); expect($state.current.name).toEqual(''); @@ -168,17 +182,33 @@ describe('uiStateRef', function() { expect($state.$current.name).toEqual(''); triggerClick(el, { button: 1 }); + timeoutFlush(); $q.flush(); expect($state.current.name).toEqual(''); expect($stateParams).toEqual({ id: "5" }); })); - it('should not transition states when element has target specified', inject(function($state, $stateParams, $document, $q, $timeout) { + it('should not transition states when element has target specified', inject(function($state, $stateParams, $document, $q) { el.attr('target', '_blank'); expect($state.$current.name).toEqual(''); triggerClick(el); + timeoutFlush(); + $q.flush(); + + expect($state.current.name).toEqual(''); + expect($stateParams).toEqual({ id: "5" }); + })); + + it('should not transition states if preventDefault() is called in click handler', inject(function($state, $stateParams, $document, $q) { + expect($state.$current.name).toEqual(''); + el.bind('click', function(e) { + e.preventDefault(); + }); + + triggerClick(el); + timeoutFlush(); $q.flush(); expect($state.current.name).toEqual('');