mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-03-17 07:40:22 +00:00
feat($resource): expose promise based api via $then and $resolved
Expose $then and $resolved properties on resource action return values which
allow checking if a promise has been resolved already as well as registering
listeners at any time of the resource object life-cycle.
This commit replaces unreleased commit f3bff27460
which exposed unintuitive $q api instead and didn't expose important stuff
like http headers.
This commit is contained in:
parent
c0a0781425
commit
dba6bc73e8
2 changed files with 238 additions and 64 deletions
|
|
@ -19,7 +19,7 @@
|
|||
* the need to interact with the low level {@link ng.$http $http} service.
|
||||
*
|
||||
* @param {string} url A parameterized URL template with parameters prefixed by `:` as in
|
||||
* `/user/:username`. If you are using a URL with a port number (e.g.
|
||||
* `/user/:username`. If you are using a URL with a port number (e.g.
|
||||
* `http://example.com:8080/api`), you'll need to escape the colon character before the port
|
||||
* number, like this: `$resource('http://example.com\\:8080/api')`.
|
||||
*
|
||||
|
|
@ -109,10 +109,23 @@
|
|||
* - non-GET "class" actions: `Resource.action([parameters], postData, [success], [error])`
|
||||
* - non-GET instance actions: `instance.$action([parameters], [success], [error])`
|
||||
*
|
||||
* The Resource also has these properties:
|
||||
*
|
||||
* - '$q': the promise from the underlying {@link ng.$http} call.
|
||||
* - '$resolved': true if the promise has been resolved (either with success or rejection);
|
||||
* The Resource instances and collection have these additional properties:
|
||||
*
|
||||
* - `$then`: the `then` method of a {@link ng.$q promise} derived from the underlying
|
||||
* {@link ng.$http $http} call.
|
||||
*
|
||||
* The success callback for the `$then` method will be resolved if the underlying `$http` requests
|
||||
* succeeds.
|
||||
*
|
||||
* The success callback is called with a single object which is the {@link ng.$http http response}
|
||||
* object extended with a new property `resource`. This `resource` property is a reference to the
|
||||
* result of the resource action — resource object or array of resources.
|
||||
*
|
||||
* The error callback is called with the {@link ng.$http http response} object when an http
|
||||
* error occurs.
|
||||
*
|
||||
* - `$resolved`: true if the promise has been resolved (either with success or rejection);
|
||||
* Knowing if the Resource has been resolved is useful in data-binding.
|
||||
*
|
||||
* @example
|
||||
|
|
@ -415,32 +428,36 @@ angular.module('ngResource', ['ng']).
|
|||
httpConfig.data = data;
|
||||
httpConfig.url = route.url(extend({}, extractParams(data, action.params || {}), params))
|
||||
|
||||
function markResolved() { value.$resolved = true; };
|
||||
function markResolved() { value.$resolved = true; }
|
||||
|
||||
promise = $http(httpConfig);
|
||||
value.$q = promise;
|
||||
value.$resolved = false;
|
||||
promise.then(markResolved, markResolved)
|
||||
promise.then(function(response) {
|
||||
var data = response.data;
|
||||
var q = value.$q, resolved = value.$resolved;
|
||||
if (data) {
|
||||
if (action.isArray) {
|
||||
value.length = 0;
|
||||
forEach(data, function(item) {
|
||||
value.push(new Resource(item));
|
||||
});
|
||||
} else {
|
||||
copy(data, value);
|
||||
value.$q = q;
|
||||
value.$resolved = resolved;
|
||||
}
|
||||
|
||||
promise.then(markResolved, markResolved);
|
||||
value.$then = promise.then(function(response) {
|
||||
var data = response.data;
|
||||
var then = value.$then, resolved = value.$resolved;
|
||||
|
||||
if (data) {
|
||||
if (action.isArray) {
|
||||
value.length = 0;
|
||||
forEach(data, function(item) {
|
||||
value.push(new Resource(item));
|
||||
});
|
||||
} else {
|
||||
copy(data, value);
|
||||
value.$then = then;
|
||||
value.$resolved = resolved;
|
||||
}
|
||||
(success||noop)(value, response.headers);
|
||||
}, error);
|
||||
}
|
||||
|
||||
(success||noop)(value, response.headers);
|
||||
|
||||
response.resource = value;
|
||||
return response;
|
||||
}, error).then;
|
||||
|
||||
return value;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -419,51 +419,208 @@ describe("resource", function() {
|
|||
expect(person.name).toEqual('misko');
|
||||
});
|
||||
|
||||
it("should have $q and $resolved properties for get", function () {
|
||||
$httpBackend.expect('GET', '/CreditCard/123').respond({id: 123, number: '9876'});
|
||||
var cc = CreditCard.get({id: 123}, callback);
|
||||
expect(cc.$q).toBeDefined();
|
||||
expect(typeof cc.$q).toBe('object');
|
||||
expect(cc.$resolved).toBeFalsy();
|
||||
$httpBackend.flush();
|
||||
expect(cc.$q).toBeDefined();
|
||||
expect(cc.$resolved).toBeTruthy();
|
||||
});
|
||||
|
||||
it("should have $q and $resolved properties for query", function() {
|
||||
$httpBackend.expect('GET', '/CreditCard?key=value').respond([{id: 1}, {id: 2}]);
|
||||
describe('promise api', function() {
|
||||
|
||||
var ccs = CreditCard.query({key: 'value'}, callback);
|
||||
expect(ccs.$q).toBeDefined();
|
||||
expect(typeof ccs.$q).toBe('object');
|
||||
expect(ccs.$resolved).toBeFalsy();
|
||||
$httpBackend.flush();
|
||||
expect(ccs.$q).toBeDefined();
|
||||
expect(ccs.$resolved).toBeTruthy();
|
||||
});
|
||||
var $rootScope;
|
||||
|
||||
it("should have $q and $resolved properties for save", function() {
|
||||
$httpBackend.expect('POST', '/CreditCard/123', '{"id":{"key":123},"name":"misko"}').
|
||||
respond({id: {key: 123}, name: 'rama'});
|
||||
|
||||
var cc = CreditCard.save({id: {key: 123}, name: 'misko'}, callback);
|
||||
expect(cc.$q).toBeDefined();
|
||||
expect(typeof cc.$q).toBe('object');
|
||||
expect(cc.$resolved).toBeFalsy();
|
||||
$httpBackend.flush();
|
||||
expect(cc.$q).toBeDefined();
|
||||
expect(cc.$resolved).toBeTruthy();
|
||||
});
|
||||
beforeEach(inject(function(_$rootScope_) {
|
||||
$rootScope = _$rootScope_;
|
||||
}));
|
||||
|
||||
it('should should have $q and $resolved properties for delete', function() {
|
||||
$httpBackend.expect('DELETE', '/CreditCard/123').respond({});
|
||||
var removed = CreditCard.remove({id:123}, callback);
|
||||
expect(removed.$q).toBeDefined();
|
||||
expect(typeof removed.$q).toBe('object');
|
||||
expect(removed.$resolved).toBeFalsy();
|
||||
$httpBackend.flush();
|
||||
expect(removed.$q).toBeDefined();
|
||||
expect(removed.$resolved).toBeTruthy();
|
||||
|
||||
describe('single resource', function() {
|
||||
|
||||
it('should add promise $then method to the result object', function() {
|
||||
$httpBackend.expect('GET', '/CreditCard/123').respond({id: 123, number: '9876'});
|
||||
var cc = CreditCard.get({id: 123});
|
||||
|
||||
cc.$then(callback);
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
|
||||
$httpBackend.flush();
|
||||
|
||||
var response = callback.mostRecentCall.args[0];
|
||||
|
||||
expect(response).toEqualData({
|
||||
data: {id: 123, number: '9876'},
|
||||
status: 200,
|
||||
config: {method: 'GET', data: undefined, url: '/CreditCard/123'},
|
||||
resource: {id: 123, number: '9876', $resolved: true}
|
||||
});
|
||||
expect(typeof response.resource.$save).toBe('function');
|
||||
});
|
||||
|
||||
|
||||
it('should keep $then around after promise resolution', function() {
|
||||
$httpBackend.expect('GET', '/CreditCard/123').respond({id: 123, number: '9876'});
|
||||
var cc = CreditCard.get({id: 123});
|
||||
|
||||
cc.$then(callback);
|
||||
$httpBackend.flush();
|
||||
|
||||
var response = callback.mostRecentCall.args[0];
|
||||
|
||||
callback.reset();
|
||||
|
||||
cc.$then(callback);
|
||||
$rootScope.$apply(); //flush async queue
|
||||
|
||||
expect(callback).toHaveBeenCalledOnceWith(response);
|
||||
});
|
||||
|
||||
|
||||
it('should allow promise chaining via $then method', function() {
|
||||
$httpBackend.expect('GET', '/CreditCard/123').respond({id: 123, number: '9876'});
|
||||
var cc = CreditCard.get({id: 123});
|
||||
|
||||
cc.$then(function(response) { return 'new value'; }).then(callback);
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(callback).toHaveBeenCalledOnceWith('new value');
|
||||
});
|
||||
|
||||
|
||||
it('should allow error callback registration via $then method', function() {
|
||||
$httpBackend.expect('GET', '/CreditCard/123').respond(404, 'resource not found');
|
||||
var cc = CreditCard.get({id: 123});
|
||||
|
||||
cc.$then(null, callback);
|
||||
$httpBackend.flush();
|
||||
|
||||
var response = callback.mostRecentCall.args[0];
|
||||
|
||||
expect(response).toEqualData({
|
||||
data : 'resource not found',
|
||||
status : 404,
|
||||
config : { method : 'GET', data : undefined, url : '/CreditCard/123' }
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should add $resolved boolean field to the result object', function() {
|
||||
$httpBackend.expect('GET', '/CreditCard/123').respond({id: 123, number: '9876'});
|
||||
var cc = CreditCard.get({id: 123});
|
||||
|
||||
expect(cc.$resolved).toBe(false);
|
||||
|
||||
cc.$then(callback);
|
||||
expect(cc.$resolved).toBe(false);
|
||||
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(cc.$resolved).toBe(true);
|
||||
});
|
||||
|
||||
|
||||
it('should set $resolved field to true when an error occurs', function() {
|
||||
$httpBackend.expect('GET', '/CreditCard/123').respond(404, 'resource not found');
|
||||
var cc = CreditCard.get({id: 123});
|
||||
|
||||
cc.$then(null, callback);
|
||||
$httpBackend.flush();
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
expect(cc.$resolved).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('resource collection', function() {
|
||||
|
||||
it('should add promise $then method to the result object', function() {
|
||||
$httpBackend.expect('GET', '/CreditCard?key=value').respond([{id: 1}, {id: 2}]);
|
||||
var ccs = CreditCard.query({key: 'value'});
|
||||
|
||||
ccs.$then(callback);
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
|
||||
$httpBackend.flush();
|
||||
|
||||
var response = callback.mostRecentCall.args[0];
|
||||
|
||||
expect(response).toEqualData({
|
||||
data: [{id: 1}, {id :2}],
|
||||
status: 200,
|
||||
config: {method: 'GET', data: undefined, url: '/CreditCard?key=value'},
|
||||
resource: [ { id : 1 }, { id : 2 } ]
|
||||
});
|
||||
expect(typeof response.resource[0].$save).toBe('function');
|
||||
expect(typeof response.resource[1].$save).toBe('function');
|
||||
});
|
||||
|
||||
|
||||
it('should keep $then around after promise resolution', function() {
|
||||
$httpBackend.expect('GET', '/CreditCard?key=value').respond([{id: 1}, {id: 2}]);
|
||||
var ccs = CreditCard.query({key: 'value'});
|
||||
|
||||
ccs.$then(callback);
|
||||
$httpBackend.flush();
|
||||
|
||||
var response = callback.mostRecentCall.args[0];
|
||||
|
||||
callback.reset();
|
||||
|
||||
ccs.$then(callback);
|
||||
$rootScope.$apply(); //flush async queue
|
||||
|
||||
expect(callback).toHaveBeenCalledOnceWith(response);
|
||||
});
|
||||
|
||||
|
||||
it('should allow promise chaining via $then method', function() {
|
||||
$httpBackend.expect('GET', '/CreditCard?key=value').respond([{id: 1}, {id: 2}]);
|
||||
var ccs = CreditCard.query({key: 'value'});
|
||||
|
||||
ccs.$then(function(response) { return 'new value'; }).then(callback);
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(callback).toHaveBeenCalledOnceWith('new value');
|
||||
});
|
||||
|
||||
|
||||
it('should allow error callback registration via $then method', function() {
|
||||
$httpBackend.expect('GET', '/CreditCard?key=value').respond(404, 'resource not found');
|
||||
var ccs = CreditCard.query({key: 'value'});
|
||||
|
||||
ccs.$then(null, callback);
|
||||
$httpBackend.flush();
|
||||
|
||||
var response = callback.mostRecentCall.args[0];
|
||||
|
||||
expect(response).toEqualData({
|
||||
data : 'resource not found',
|
||||
status : 404,
|
||||
config : { method : 'GET', data : undefined, url : '/CreditCard?key=value' }
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should add $resolved boolean field to the result object', function() {
|
||||
$httpBackend.expect('GET', '/CreditCard?key=value').respond([{id: 1}, {id: 2}]);
|
||||
var ccs = CreditCard.query({key: 'value'}, callback);
|
||||
|
||||
expect(ccs.$resolved).toBe(false);
|
||||
|
||||
ccs.$then(callback);
|
||||
expect(ccs.$resolved).toBe(false);
|
||||
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(ccs.$resolved).toBe(true);
|
||||
});
|
||||
|
||||
|
||||
it('should set $resolved field to true when an error occurs', function() {
|
||||
$httpBackend.expect('GET', '/CreditCard?key=value').respond(404, 'resource not found');
|
||||
var ccs = CreditCard.query({key: 'value'});
|
||||
|
||||
ccs.$then(null, callback);
|
||||
$httpBackend.flush();
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
expect(ccs.$resolved).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue