28

我必须为输出 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

4

4 回答 4

17

你有几个选择。如果您可以更改服务器输出,则可以将元信息(计数、下一个、上一个)添加为标头值,而不是将它们添加到响应正文中。

您的第二个选择是使用 transformResponse 转换响应。这在 angular v1.1.2 及更高版本(不稳定分支)中作为 $response 配置提供:

var Data = $resource('./data.json',{},{
  list:{isArray:true,method:'get',
    transformResponse: function (data, headers) {
      return JSON.parse(data).results; 
   }}
});

如果您不想使用不稳定的分支,也可以更改 $resource 使用的 $http:

$http.defaults.transformResponse.push(function(data){
  if(data && data.results){
    return data.results;
  }
});

我用这两个例子创建了一个 plunker:http ://plnkr.co/edit/PmYotP0eo5k41Z6ZCxl4?p=preview

我不确定将元数据传递给应用程序其余部分的最佳方法(如果需要)。您可以将其附加到第一个结果中,或者将其添加为单独的对象 - 也许不那么优雅,但它会完成工作。

于 2013-05-09T17:34:30.790 回答
16

我知道这个问题有点老了。但我认为答案并未涵盖主要问题 - 如何获取分页信息以及如何保留对象列表的资源功能。

您基本上有两种解决方案,将分页器数据传递到转换消息的标题中,或者使用 $http 然后手动实例化项目。

1.转换消息

在这里,我重新定义查询以将分页数据放入标题中。

Headers 不是一个数组——它是“headersGetter”,它通过调用 headers('Header-Name') 返回 header,并通过调用 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并实例化Resource

当使用 $http 代替资源时,存在一个问题,您仍然希望将数组元素用作资源实例并能够调用 $save / $delete,因此可以手动实例化它们。在这里你也可以像往常一样使用普通的promise。

.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
                  }
                };
              });
         };

我更喜欢第二种解决方案,因为它更简单。

于 2015-03-17T23:28:18.537 回答
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;
      }
    },
  });
于 2016-04-18T10:20:51.487 回答
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;
  })
于 2015-06-29T13:54:51.070 回答