Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit 05772e1

Browse files
ashtuchkinvojtajina
authored andcommitted
feat($resource): expose promise instead of only $then
- Instance or collection have `$promise` property which is the initial promise. - Add per-action `interceptor`, which has access to entire $http response object. BREAKING CHANGE: resource instance does not have `$then` function anymore. Before: Resource.query().$then(callback); After: Resource.query().$promise.then(callback); BREAKING CHANGE: instance methods return the promise rather than the instance itself. Before: resource.$save().chaining = true; After: resource.$save(); resourve.chaining = true; BREAKING CHANGE: On success, promise is resolved with the resource instance rather than http response object. Use interceptor to access the http response object. Before: Resource.query().$then(function(response) {...}); After: var Resource = $resource('/url', {}, { get: { method: 'get', interceptor: { response: function(response) { // expose response return response; } } } });
1 parent da5f537 commit 05772e1

File tree

2 files changed

+227
-101
lines changed

2 files changed

+227
-101
lines changed

src/ngResource/resource.js

+66-60
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,9 @@
9292
* requests with credentials} for more information.
9393
* - **`responseType`** - `{string}` - see {@link
9494
* https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#responseType requestType}.
95+
* - **`interceptor`** - `{Object=}` - The interceptor object has two optional methods -
96+
* `response` and `responseError`. Both `response` and `responseError` interceptors get called
97+
* with `http response` object. See {@link ng.$http $http interceptors}.
9598
*
9699
* @returns {Object} A resource "class" object with methods for the default set of resource actions
97100
* optionally extended with custom `actions`. The default set contains these actions:
@@ -130,24 +133,27 @@
130133
* - non-GET "class" actions: `Resource.action([parameters], postData, [success], [error])`
131134
* - non-GET instance actions: `instance.$action([parameters], [success], [error])`
132135
*
136+
* Success callback is called with (value, responseHeaders) arguments. Error callback is called
137+
* with (httpResponse) argument.
133138
*
134-
* The Resource instances and collection have these additional properties:
139+
* Class actions return empty instance (with additional properties below).
140+
* Instance actions return promise of the action.
135141
*
136-
* - `$then`: the `then` method of a {@link ng.$q promise} derived from the underlying
137-
* {@link ng.$http $http} call.
142+
* The Resource instances and collection have these additional properties:
138143
*
139-
* The success callback for the `$then` method will be resolved if the underlying `$http` requests
140-
* succeeds.
144+
* - `$promise`: the {@link ng.$q promise} of the original server interaction that created this
145+
* instance or collection.
141146
*
142-
* The success callback is called with a single object which is the {@link ng.$http http response}
143-
* object extended with a new property `resource`. This `resource` property is a reference to the
144-
* result of the resource action — resource object or array of resources.
147+
* On success, the promise is resolved with the same resource instance or collection object,
148+
* updated with data from server. This makes it easy to use in
149+
* {@link ng.$routeProvider resolve section of $routeProvider.when()} to defer view rendering
150+
* until the resource(s) are loaded.
145151
*
146-
* The error callback is called with the {@link ng.$http http response} object when an http
147-
* error occurs.
152+
* On failure, the promise is resolved with the {@link ng.$http http response} object,
153+
* without the `resource` property.
148154
*
149-
* - `$resolved`: true if the promise has been resolved (either with success or rejection);
150-
* Knowing if the Resource has been resolved is useful in data-binding.
155+
* - `$resolved`: `true` after first server interaction is completed (either with success or rejection),
156+
* `false` before that. Knowing if the Resource has been resolved is useful in data-binding.
151157
*
152158
* @example
153159
*
@@ -268,7 +274,7 @@
268274
</doc:example>
269275
*/
270276
angular.module('ngResource', ['ng']).
271-
factory('$resource', ['$http', '$parse', function($http, $parse) {
277+
factory('$resource', ['$http', '$parse', '$q', function($http, $parse, $q) {
272278
var DEFAULT_ACTIONS = {
273279
'get': {method:'GET'},
274280
'save': {method:'POST'},
@@ -398,19 +404,19 @@ angular.module('ngResource', ['ng']).
398404
return ids;
399405
}
400406

407+
function defaultResponseInterceptor(response) {
408+
return response.resource;
409+
}
410+
401411
function Resource(value){
402412
copy(value || {}, this);
403413
}
404414

405415
forEach(actions, function(action, name) {
406-
action.method = angular.uppercase(action.method);
407-
var hasBody = action.method == 'POST' || action.method == 'PUT' || action.method == 'PATCH';
416+
var hasBody = /^(POST|PUT|PATCH)$/i.test(action.method);
417+
408418
Resource[name] = function(a1, a2, a3, a4) {
409-
var params = {};
410-
var data;
411-
var success = noop;
412-
var error = null;
413-
var promise;
419+
var params = {}, data, success, error;
414420

415421
switch(arguments.length) {
416422
case 4:
@@ -442,31 +448,28 @@ angular.module('ngResource', ['ng']).
442448
break;
443449
case 0: break;
444450
default:
445-
throw "Expected between 0-4 arguments [params, data, success, error], got " +
451+
throw "Expected up to 4 arguments [params, data, success, error], got " +
446452
arguments.length + " arguments.";
447453
}
448454

449-
var value = this instanceof Resource ? this : (action.isArray ? [] : new Resource(data));
450-
var httpConfig = {},
451-
promise;
455+
var isInstanceCall = data instanceof Resource;
456+
var value = isInstanceCall ? data : (action.isArray ? [] : new Resource(data));
457+
var httpConfig = {};
458+
var responseInterceptor = action.interceptor && action.interceptor.response || defaultResponseInterceptor;
459+
var responseErrorInterceptor = action.interceptor && action.interceptor.responseError || undefined;
452460

453461
forEach(action, function(value, key) {
454-
if (key != 'params' && key != 'isArray' ) {
462+
if (key != 'params' && key != 'isArray' && key != 'interceptor') {
455463
httpConfig[key] = copy(value);
456464
}
457465
});
466+
458467
httpConfig.data = data;
459468
route.setUrlParams(httpConfig, extend({}, extractParams(data, action.params || {}), params), action.url);
460469

461-
function markResolved() { value.$resolved = true; }
462-
463-
promise = $http(httpConfig);
464-
value.$resolved = false;
465-
466-
promise.then(markResolved, markResolved);
467-
value.$then = promise.then(function(response) {
468-
var data = response.data;
469-
var then = value.$then, resolved = value.$resolved;
470+
var promise = $http(httpConfig).then(function(response) {
471+
var data = response.data,
472+
promise = value.$promise;
470473

471474
if (data) {
472475
if (action.isArray) {
@@ -476,44 +479,47 @@ angular.module('ngResource', ['ng']).
476479
});
477480
} else {
478481
copy(data, value);
479-
value.$then = then;
480-
value.$resolved = resolved;
482+
value.$promise = promise;
481483
}
482484
}
483485

486+
value.$resolved = true;
487+
484488
(success||noop)(value, response.headers);
485489

486490
response.resource = value;
491+
487492
return response;
488-
}, error).then;
493+
}, function(response) {
494+
value.$resolved = true;
489495

490-
return value;
491-
};
496+
(error||noop)(response);
492497

498+
return $q.reject(response);
499+
}).then(responseInterceptor, responseErrorInterceptor);
493500

494-
Resource.prototype['$' + name] = function(a1, a2, a3) {
495-
var params = extractParams(this),
496-
success = noop,
497-
error;
498501

499-
switch(arguments.length) {
500-
case 3: params = a1; success = a2; error = a3; break;
501-
case 2:
502-
case 1:
503-
if (isFunction(a1)) {
504-
success = a1;
505-
error = a2;
506-
} else {
507-
params = a1;
508-
success = a2 || noop;
509-
}
510-
case 0: break;
511-
default:
512-
throw "Expected between 1-3 arguments [params, success, error], got " +
513-
arguments.length + " arguments.";
502+
if (!isInstanceCall) {
503+
// we are creating instance / collection
504+
// - set the initial promise
505+
// - return the instance / collection
506+
value.$promise = promise;
507+
value.$resolved = false;
508+
509+
return value;
510+
}
511+
512+
// instance call
513+
return promise;
514+
};
515+
516+
517+
Resource.prototype['$' + name] = function(params, success, error) {
518+
if (isFunction(params)) {
519+
error = success; success = params; params = {};
514520
}
515-
var data = hasBody ? this : undefined;
516-
Resource[name].call(this, params, data, success, error);
521+
var result = Resource[name](params, this, success, error);
522+
return result.$promise || result;
517523
};
518524
});
519525

0 commit comments

Comments
 (0)