@@ -63,6 +63,8 @@ function shallowClearAndCopy(src, dst) {
63
63
* @ngdoc service
64
64
* @name $resource
65
65
* @requires $http
66
+ * @requires ng.$log
67
+ * @requires $q
66
68
*
67
69
* @description
68
70
* A factory which creates a resource object that lets you interact with
@@ -107,9 +109,9 @@ function shallowClearAndCopy(src, dst) {
107
109
* URL `/path/greet?salutation=Hello`.
108
110
*
109
111
* If the parameter value is prefixed with `@` then the value for that parameter will be extracted
110
- * from the corresponding property on the `data` object (provided when calling an action method). For
111
- * example, if the `defaultParam` object is `{someParam: '@someProp '}` then the value of `someParam`
112
- * will be `data.someProp`.
112
+ * from the corresponding property on the `data` object (provided when calling an action method).
113
+ * For example, if the `defaultParam` object is `{someParam: '@someProp '}` then the value of
114
+ * `someParam` will be `data.someProp`.
113
115
*
114
116
* @param {Object.<Object>= } actions Hash with declaration of custom actions that should extend
115
117
* the default set of resource actions. The declaration should be created in the format of {@link
@@ -143,15 +145,23 @@ function shallowClearAndCopy(src, dst) {
143
145
* `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
144
146
* transform function or an array of such functions. The transform function takes the http
145
147
* response body and headers and returns its transformed (typically deserialized) version.
146
- * By default, transformResponse will contain one function that checks if the response looks like
147
- * a JSON string and deserializes it using `angular.fromJson`. To prevent this behavior, set
148
- * `transformResponse` to an empty array: `transformResponse: []`
148
+ * By default, transformResponse will contain one function that checks if the response looks
149
+ * like a JSON string and deserializes it using `angular.fromJson`. To prevent this behavior,
150
+ * set `transformResponse` to an empty array: `transformResponse: []`
149
151
* - **`cache`** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the
150
152
* GET request, otherwise if a cache instance built with
151
153
* {@link ng.$cacheFactory $cacheFactory}, this cache will be used for
152
154
* caching.
153
- * - **`timeout`** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise} that
154
- * should abort the request when resolved.
155
+ * - **`timeout`** – `{number}` – timeout in milliseconds.<br />
156
+ * **Note:** In contrast to {@link ng.$http#usage $http.config}, {@link ng.$q promises} are
157
+ * **not** supported in $resource, because the same value has to be re-used for multiple
158
+ * requests. If you are looking for a way to cancel requests, you should use the `cancellable`
159
+ * option.
160
+ * - **`cancellable`** – `{boolean}` – if set to true, the request made by a "non-instance" call
161
+ * will be cancelled (if not already completed) by calling `$cancelRequest()` on the call's
162
+ * return value. Calling `$cancelRequest()` for a non-cancellable or an already
163
+ * completed/cancelled request will have no effect.<br />
164
+ * **Note:** If a timeout is specified in millisecondes, `cancellable` is ignored.
155
165
* - **`withCredentials`** - `{boolean}` - whether to set the `withCredentials` flag on the
156
166
* XHR object. See
157
167
* [requests with credentials](https://developer.mozilla.org/en/http_access_control#section_5)
@@ -163,12 +173,13 @@ function shallowClearAndCopy(src, dst) {
163
173
* with `http response` object. See {@link ng.$http $http interceptors}.
164
174
*
165
175
* @param {Object } options Hash with custom settings that should extend the
166
- * default `$resourceProvider` behavior. The only supported option is
167
- *
168
- * Where:
176
+ * default `$resourceProvider` behavior. The supported options are:
169
177
*
170
178
* - **`stripTrailingSlashes`** – {boolean} – If true then the trailing
171
179
* slashes from any calculated URL will be stripped. (Defaults to true.)
180
+ * - **`cancellable`** – {boolean} – If true, the request made by a "non-instance" call will be
181
+ * cancelled (if not already completed) by calling `$cancelRequest()` on the call's return value.
182
+ * This can be overwritten per action. (Defaults to false.)
172
183
*
173
184
* @returns {Object } A resource "class" object with methods for the default set of resource actions
174
185
* optionally extended with custom `actions`. The default set contains these actions:
@@ -216,7 +227,7 @@ function shallowClearAndCopy(src, dst) {
216
227
* Class actions return empty instance (with additional properties below).
217
228
* Instance actions return promise of the action.
218
229
*
219
- * The Resource instances and collection have these additional properties:
230
+ * The Resource instances and collections have these additional properties:
220
231
*
221
232
* - `$promise`: the {@link ng.$q promise} of the original server interaction that created this
222
233
* instance or collection.
@@ -236,6 +247,11 @@ function shallowClearAndCopy(src, dst) {
236
247
* rejection), `false` before that. Knowing if the Resource has been resolved is useful in
237
248
* data-binding.
238
249
*
250
+ * The Resource instances and collections have these additional methods:
251
+ *
252
+ * - `$cancelRequest`: If there is a cancellable, pending request related to the instance or
253
+ * collection, calling this method will abort the request.
254
+ *
239
255
* @example
240
256
*
241
257
* # Credit card resource
@@ -280,6 +296,11 @@ function shallowClearAndCopy(src, dst) {
280
296
*
281
297
* Calling these methods invoke `$http` on the `url` template with the given `method`, `params` and
282
298
* `headers`.
299
+ *
300
+ * @example
301
+ *
302
+ * # User resource
303
+ *
283
304
* When the data is returned from the server then the object is an instance of the resource type and
284
305
* all of the non-GET methods are available with `$` prefix. This allows you to easily support CRUD
285
306
* operations (create, read, update, delete) on server-side data.
@@ -298,10 +319,10 @@ function shallowClearAndCopy(src, dst) {
298
319
*
299
320
```js
300
321
var User = $resource('/user/:userId', {userId:'@id '});
301
- User.get({userId:123}, function(u , getResponseHeaders){
302
- u .abc = true;
303
- u .$save(function(u , putResponseHeaders) {
304
- //u => saved user object
322
+ User.get({userId:123}, function(user , getResponseHeaders){
323
+ user .abc = true;
324
+ user .$save(function(user , putResponseHeaders) {
325
+ //user => saved user object
305
326
//putResponseHeaders => $http header getter
306
327
});
307
328
});
@@ -316,8 +337,11 @@ function shallowClearAndCopy(src, dst) {
316
337
$scope.user = user;
317
338
});
318
339
```
319
-
340
+ *
341
+ * @example
342
+ *
320
343
* # Creating a custom 'PUT' request
344
+ *
321
345
* In this example we create a custom method on our resource to make a PUT request
322
346
* ```js
323
347
* var app = angular.module('app', ['ngResource', 'ngRoute']);
@@ -345,6 +369,34 @@ function shallowClearAndCopy(src, dst) {
345
369
* // This will PUT /notes/ID with the note object in the request payload
346
370
* }]);
347
371
* ```
372
+ *
373
+ * @example
374
+ *
375
+ * # Cancelling requests
376
+ *
377
+ * If an action's configuration specifies that it is cancellable, you can cancel the request related
378
+ * to an instance or collection (as long as it is a result of a "non-instance" call):
379
+ *
380
+ ```js
381
+ // ...defining the `Hotel` resource...
382
+ var Hotel = $resource('/api/hotel/:id', {id: '@id' }, {
383
+ // Let's make the `query()` method cancellable
384
+ query: {method: 'get', isArray: true, cancellable: true}
385
+ });
386
+
387
+ // ...somewhere in the PlanVacationController...
388
+ ...
389
+ this.onDestinationChanged = function onDestinationChanged(destination) {
390
+ // We don't care about any pending request for hotels
391
+ // in a different destination any more
392
+ this.availableHotels.$cancelRequest();
393
+
394
+ // Let's query for hotels in '<destination>'
395
+ // (calls: /api/hotel?location=<destination>)
396
+ this.availableHotels = Hotel.query({location: destination});
397
+ };
398
+ ```
399
+ *
348
400
*/
349
401
angular . module ( 'ngResource' , [ 'ng' ] ) .
350
402
provider ( '$resource' , function ( ) {
@@ -365,7 +417,7 @@ angular.module('ngResource', ['ng']).
365
417
}
366
418
} ;
367
419
368
- this . $get = [ '$http' , '$q' , function ( $http , $q ) {
420
+ this . $get = [ '$http' , '$log' , '$ q', function ( $http , $log , $q ) {
369
421
370
422
var noop = angular . noop ,
371
423
forEach = angular . forEach ,
@@ -524,6 +576,22 @@ angular.module('ngResource', ['ng']).
524
576
525
577
forEach ( actions , function ( action , name ) {
526
578
var hasBody = / ^ ( P O S T | P U T | P A T C H ) $ / i. test ( action . method ) ;
579
+ var cancellable ;
580
+
581
+ if ( angular . isNumber ( action . timeout ) ) {
582
+ cancellable = false ;
583
+ } else if ( action . timeout ) {
584
+ $log . debug ( 'ngResource:\n' +
585
+ ' Only numeric values are allowed as `timeout`.\n' +
586
+ ' Promises are not supported in $resource, because the same value has to ' +
587
+ 'be re-used for multiple requests. If you are looking for a way to cancel ' +
588
+ 'requests, you should use the `cancellable` option.' ) ;
589
+ delete action . timeout ;
590
+ } else {
591
+ cancellable = angular . isDefined ( action . cancellable ) ? action . cancellable :
592
+ ( options && angular . isDefined ( options . cancellable ) ) ? options . cancellable :
593
+ provider . defaults . cancellable ;
594
+ }
527
595
528
596
Resource [ name ] = function ( a1 , a2 , a3 , a4 ) {
529
597
var params = { } , data , success , error ;
@@ -572,6 +640,7 @@ angular.module('ngResource', ['ng']).
572
640
defaultResponseInterceptor ;
573
641
var responseErrorInterceptor = action . interceptor && action . interceptor . responseError ||
574
642
undefined ;
643
+ var timeoutDeferred ;
575
644
576
645
forEach ( action , function ( value , key ) {
577
646
switch ( key ) {
@@ -581,21 +650,23 @@ angular.module('ngResource', ['ng']).
581
650
case 'params' :
582
651
case 'isArray' :
583
652
case 'interceptor' :
584
- break ;
585
- case 'timeout' :
586
- httpConfig [ key ] = value ;
653
+ case 'cancellable' :
587
654
break ;
588
655
}
589
656
} ) ;
590
657
658
+ if ( ! isInstanceCall && cancellable ) {
659
+ timeoutDeferred = $q . defer ( ) ;
660
+ httpConfig . timeout = timeoutDeferred . promise ;
661
+ }
662
+
591
663
if ( hasBody ) httpConfig . data = data ;
592
664
route . setUrlParams ( httpConfig ,
593
665
extend ( { } , extractParams ( data , action . params || { } ) , params ) ,
594
666
action . url ) ;
595
667
596
668
var promise = $http ( httpConfig ) . then ( function ( response ) {
597
- var data = response . data ,
598
- promise = value . $promise ;
669
+ var data = response . data ;
599
670
600
671
if ( data ) {
601
672
// Need to convert action.isArray to boolean in case it is undefined
@@ -620,24 +691,27 @@ angular.module('ngResource', ['ng']).
620
691
}
621
692
} ) ;
622
693
} else {
694
+ var promise = value . $promise ; // Save the promise
623
695
shallowClearAndCopy ( data , value ) ;
624
- value . $promise = promise ;
696
+ value . $promise = promise ; // Restore the promise
625
697
}
626
698
}
627
-
628
- value . $resolved = true ;
629
-
630
699
response . resource = value ;
631
700
632
701
return response ;
633
702
} , function ( response ) {
634
- value . $resolved = true ;
635
-
636
703
( error || noop ) ( response ) ;
637
-
638
704
return $q . reject ( response ) ;
639
705
} ) ;
640
706
707
+ promise . finally ( function ( ) {
708
+ value . $resolved = true ;
709
+ if ( cancellable ) {
710
+ value . $cancelRequest = angular . noop ;
711
+ timeoutDeferred = httpConfig . timeout = null ;
712
+ }
713
+ } ) ;
714
+
641
715
promise = promise . then (
642
716
function ( response ) {
643
717
var value = responseInterceptor ( response ) ;
@@ -652,6 +726,7 @@ angular.module('ngResource', ['ng']).
652
726
// - return the instance / collection
653
727
value . $promise = promise ;
654
728
value . $resolved = false ;
729
+ if ( cancellable ) value . $cancelRequest = timeoutDeferred . resolve ;
655
730
656
731
return value ;
657
732
}
0 commit comments