diff --git a/src/ngResource/resource.js b/src/ngResource/resource.js index 8874bbeca484..79281954dd1c 100644 --- a/src/ngResource/resource.js +++ b/src/ngResource/resource.js @@ -202,6 +202,8 @@ function shallowClearAndCopy(src, dst) { * rejection), `false` before that. Knowing if the Resource has been resolved is useful in * data-binding. * + * - `$abort`: a method to abort the request if it has not completed yet. + * * @example * * # Credit card resource @@ -303,7 +305,7 @@ function shallowClearAndCopy(src, dst) { * */ angular.module('ngResource', ['ng']). - factory('$resource', ['$http', '$q', function($http, $q) { + factory('$resource', ['$http', '$q', '$timeout', function($http, $q, $timeout) { var DEFAULT_ACTIONS = { 'get': {method:'GET'}, @@ -501,6 +503,17 @@ angular.module('ngResource', ['ng']). } }); + // If parameters do not contain `timeout` which is a promise, create it, + // so that later this call can be aborted by resolving this promise. + var timeout = httpConfig.timeout; + var timeoutDeferred; + if (!timeout || !timeout.then) { + timeoutDeferred = $q.defer(); + httpConfig.timeout = timeoutDeferred.promise; + // If timeout is specified in milliseconds, use it to abort via promise + if (timeout) $timeout(timeoutDeferred.resolve, timeout); + } + if (hasBody) httpConfig.data = data; route.setUrlParams(httpConfig, extend({}, extractParams(data, action.params || {}), params), @@ -557,6 +570,7 @@ angular.module('ngResource', ['ng']). // - return the instance / collection value.$promise = promise; value.$resolved = false; + if (timeoutDeferred) value.$abort = timeoutDeferred.resolve; return value; } diff --git a/test/ngResource/resourceSpec.js b/test/ngResource/resourceSpec.js index b23f0ca42bd7..c0fe645fad0a 100644 --- a/test/ngResource/resourceSpec.js +++ b/test/ngResource/resourceSpec.js @@ -674,6 +674,22 @@ describe("resource", function() { }); + it('should add $abort method to the result object', function(){ + $httpBackend.expect('POST', '/CreditCard/123!charge?amount=10', '{"auth":"abc"}').respond({success: 'ok'}); + var errorCallback = jasmine.createSpy(); + var cc = CreditCard.charge({id:123, amount:10}, {auth:'abc'}); + cc.$promise.then(callback, errorCallback); + + expect(cc.$abort).toEqual(jasmine.any(Function)); + cc.$abort(); + + $httpBackend.verifyNoOutstandingExpectation(); + $httpBackend.verifyNoOutstandingRequest(); + expect(callback).not.toHaveBeenCalled(); + expect(errorCallback).toHaveBeenCalled(); + }); + + it('should return promise from action method calls', function() { $httpBackend.expect('GET', '/CreditCard/123').respond({id: 123, number: '9876'}); var cc = new CreditCard({name: 'Mojo'}); @@ -837,6 +853,22 @@ describe("resource", function() { expect(callback).toHaveBeenCalledOnce(); expect(ccs.$resolved).toBe(true); }); + + + it('should add $abort method to the result object', function(){ + $httpBackend.expect('GET', '/CreditCard?key=value').respond([{id: 1}, {id: 2}]); + var errorCallback = jasmine.createSpy(); + var ccs = CreditCard.query({key: 'value'}); + ccs.$promise.then(callback, errorCallback); + + expect(ccs.$abort).toEqual(jasmine.any(Function)); + ccs.$abort(); + + $httpBackend.verifyNoOutstandingExpectation(); + $httpBackend.verifyNoOutstandingRequest(); + expect(callback).not.toHaveBeenCalled(); + expect(errorCallback).toHaveBeenCalled(); + }); }); it('should allow per action response interceptor that gets full response', function() {