我必须为输出JSON的API构建一个angularjs客户端,如下所示:
{
"count": 10,
"next": null,
"previous": "http://site.tld/api/items/?start=4"
"results": [
{
"url": "http://site.tld/api/items/1.json",
"title": "test",
"description": "",
"user": "http://site.tld/api/users/4.json",
"creation_datetime": "2013-05-08T14:31:43.428"
},
{
"url": "http://site.tld/api/items/2.json",
"title": "test2",
"description": "",
"user": "http://site.tld/api/users/1.json",
"creation_datetime": "2013-05-08T14:31:43.428"
},
{
"url": "http://site.tld/api/items/3.json",
"title": "test3",
"description": "",
"user": "http://site.tld/api/users/2.json",
"creation_datetime": "2013-05-08T14:31:43.428"
}
]
}
如何制作映射到此的$resource
?如果我使用isArray=false
,我会将整个blob作为一个对象,可用于阅读,但我不能在其上调用.put()
。如果我使用isArray
,则它无效。
有没有干净的方法呢?或者我应该回到使用$http
?
答案 0 :(得分:17)
你有几个选择。如果您可以更改服务器输出,则可以将元信息(count,next,previous)添加为标题值,而不是将它们添加到响应正文中。
您的第二个选择是使用transformResponse转换响应。这可以作为角度v1.1.2 and later(不稳定分支)中的$响应配置:
var Data = $resource('./data.json',{},{
list:{isArray:true,method:'get',
transformResponse: function (data, headers) {
return JSON.parse(data).results;
}}
});
如果您不想使用unstable分支,也可以更改$ resource使用的$ http:
$http.defaults.transformResponse.push(function(data){
if(data && data.results){
return data.results;
}
});
我用两个例子创建了一个plunker:http://plnkr.co/edit/PmYotP0eo5k41Z6ZCxl4?p=preview
我不确定将元数据传递给应用程序其余部分的最佳方法(如果需要)。您可以将其附加到第一个结果,或将其添加为单独的对象 - 可能不是那么优雅,但它可以完成工作。
答案 1 :(得分:16)
我知道这个问题有点老了。但我认为答案并不涵盖主要问题 - 如何获取分页信息以及如何保留资源功能以用于对象列表。
您基本上有两个解决方案,将分页器数据传递到转换消息中的标头或使用$ http然后手动实例化项目。
1.转换消息
这里我重新定义查询以将分页数据放入标题中。
标题不是一个数组 - 它是" headersGetter"它通过调用标题返回标题(' Header-Name')并通过调用headers()返回内部对象。我必须将标题设置为小写。
var PAGINATION_TOTAL = 'pagination-total-elements';
var PAGINATION_SIZE = 'pagination-size';
...
.factory('BeerResourceFactory', function($resource, API_URI) {
return $resource(API_URI + '/beer/:id',
{'id': '@id'},
{
'update': {method: 'PUT'},
'query' : {method: 'GET', isArray:true, transformResponse : function(data, headers) {
var jsonData = JSON.parse(data);
headers()[PAGINATION_TOTAL] = jsonData.totalElements;
headers()[PAGINATION_SIZE] = jsonData.size;
return jsonData.content;
}}
});
})
之后我定义了封装它的服务并从头文件中获取分页。我们不能使用$ promise.then()和重新生成结果,因为promise只获得结果作为参数而不是headersGetter,所以我们必须使用普通的回调并创建自己的promise。
.service('beerService', function(BeerResourceFactory, $q) {
this.query = function(filter) {
var defer = $q.defer();
BeerResourceFactory.query(filter, function(result, headers) {
var promiseResult = {
beers: result,
paging: {
totalItems: headers(PAGINATION_TOTAL),
perPage: headers(PAGINATION_SIZE)
}
};
defer.resolve(promiseResult);
});
return defer.promise;
}
2.使用$ http并实例化资源
当使用$ http而不是资源时,您仍然希望使用数组元素作为资源实例并且能够调用$ save / $ delete,因此可以手动实例化它们。在这里你也可以使用普通的承诺作为ussual。
.service('beerService', function($http, BeerResourceFactory, API_URI) {
this.query = function(filter) {
return $http.get(API_URI + '/beer', {params: filter})
.then(function(response) {
var transformedList = response.data.content.map(function(element) {
return new BeerResourceFactory(element);
});
return {
beers: transformedList,
paging: {
totalItems: response.data.totalElements,
perPage: response.data.size
}
};
});
};
我更喜欢第二种解决方案,因为它更简单。
答案 2 :(得分:2)
我也很难解决这个问题,这对我有用。添加一个响应变换器,它将获取数组结果并手动创建一个资源对象,我认为ngResource无论如何都会在内部进行。
var Plan = $resource(apiPath + 'plans/:planId/', {planId:'@id'}, {
query: {
isArray: false,
transformResponse: function(response){
response = angular.fromJson(response);
response.results = response.results.map(function(plan) {
return new Plan(plan);
});
return response;
}
},
});
答案 3 :(得分:0)
到目前为止,这已经更老了,但我设法在一个资源工厂解决了这个问题:
.factory('BeerResourceFactory', function($resource, API_URI) {
var resource = $resource(API_URI + '/beer/:id',
{'id': '@id'},
{
'update': {method: 'PUT'},
'query' : {method: 'GET', isArray:true, transformResponse : function(data) {
var jsonData = angular.fromJson(data);
jsonData.beers = jsonData.beers.map(function (beer) {
return new resource(beer)
});
return jsonData.content;
}}
});
return resource;
})