Как обрабатывать разбиение на страницы и подсчет с помощью ресурсов angularjs?

Мне нужно создать клиент angularjs для API, выводящего JSON следующим образом:

{
  "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?

Ответ 1

У вас есть несколько вариантов. Если вы можете изменить выход сервера, вы можете добавить метаинформацию (счетчик, следующий, предыдущий) в качестве значений заголовка вместо добавления их в тело ответа.

Второй вариант - преобразовать ответ с помощью transformResponse. Это доступно в виде конфигурации $response в angular v1.1.2 и более поздних версиях (неустойчивая ветка):

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

Если вы не хотите использовать нестабильную ветвь, также можно изменить $http, который использует ресурс $:

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

Я создал плункер с обоими примерами: http://plnkr.co/edit/PmYotP0eo5k41Z6ZCxl4?p=preview

Я не уверен, какой лучший подход для передачи метаданных остальной части вашего приложения (если вам это нужно). Вы можете добавить его к первому результату или добавить его как отдельный объект - возможно, не такой элегантный, но он выполнит эту работу.

Ответ 2

Я знаю, что этот вопрос немного стар. Но я думаю, что ответ не охватывает основную проблему - как получить информацию разбивки на страницы и как сохранить ресурсы для списка объектов.

У вас есть в основном два решения, передайте данные paginator в заголовки в сообщении трансформирования или используйте $http, а затем создайте элементы вручную.

сообщение 1.Transform

Здесь я переопределяю запрос, чтобы помещать данные разбиения на страницы в заголовки.

Заголовки - это не массив - это "headersGetter", который возвращает заголовок, вызывая заголовки ( "Заголовок заголовка" ) и возвращает внутренний объект, вызывая 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;
              }}
            });
      })

После этого я определяю службу, которая инкапсулирует это и снимает разбиение на страницы из заголовков. Судно, мы не можем использовать $expected.then() и результат возврата, потому что обещание получает только результат как аргумент, а не headersGetter, поэтому мы должны использовать обычный обратный вызов и создавать собственные обещания.

.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, поэтому их можно создать вручную. Здесь вы также можете использовать обычные обещания как обычные.

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

Я предпочел бы второе решение, потому что его более простой.

Ответ 3

Я тоже влюбился в эту проблему, и вот что сработало для меня. Добавьте ответный трансформатор, который примет результат массива и создаст объект ресурса вручную, и я предполагаю, что 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;
      }
    },
  });

Ответ 4

К настоящему времени это еще больше, но мне удалось решить это в одном ресурсе factory:

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