Angular - расширение подобъекта $resource с помощью пользовательских методов

В большинстве случаев результат <custom-resource>.query() Метод - это массив, который можно легко расширить с помощью некоторых методов (бизнес-логики) со следующим (фабричным) кодом:

var Data = $resource('http://..');
Data.prototype.foo = function() {return ...};

это идеально подходит для использования с ng-repeat / ng-class, вот так:

<tr ng-repeat="item in responseData" ng-class="{warning: item.foo()}">..</tr>

Моя проблема в том, что каждый ответ по списку инкапсулируется в объект, который, помимо фактического списка, имеет некоторые мета-свойства (информация о сортировке и т. Д.), Поэтому возвращаемый конечный объект выглядит так:

{ order_field: "name", items: [{..}, {..},{..}] }

Теперь, как мне сделать то же самое, что и ранее с ng-repeat/ng-class?

<tr ng-repeat="item in responseData.items" ng-class="????">..</tr>

предыдущий метод не будет работать, так как метод "foo" определен на responseData и НЕ на item объект

Есть ли способ напрямую расширить базовый класс, используемый для создания экземпляров объектов в списке?

Спасибо!

4 ответа

Решение

Я нашел эту проблему раньше, и решение, кажется, transformResponseДжон Ледбеттер говорит в другом ответе.

В любом случае, если вам нужно сохранить весь объект, а также заполнить массив "items" экземплярами ресурса, вы можете сделать это с помощью следующего трюка:

Взяв пример из ответа Джона и немного его модифицировав:

angular.module('foo')

  .factory('Post', ['$resource', function($resource) {

    var Post = $resource('/api/posts/:id', { id: '@id' }, {
      query: {
        method: 'GET',
        isArray: false, // <- not returning an array
        transformResponse: function(data, header) {
          var wrapped = angular.fromJson(data);
          angular.forEach(wrapped.items, function(item, idx) {
             wrapped.items[idx] = new Post(item); //<-- replace each item with an instance of the resource object
          });
          return wrapped;
        }
      }
    });

    Post.prototype.foo = function() { /* ... */ };

    return Post;
  }]);

Если вы используете angular-resource 1.1.5 (который, насколько я могу судить, на самом деле работает отлично с angular 1.0.7), есть transformResponse опция, которую вы можете указать при переопределении $resource методы:

angular.module('foo')
  .factory('Post', ['$resource', function($resource) {

    var Post = $resource('/api/posts/:id', { id: '@id' }, {
      query: {
        method: 'GET',
        isArray: true,
        transformResponse: function(data, header) {
          var wrapped = angular.fromJson(data);
          return wrapped.items;
        }
      }
    });

    Post.prototype.foo = function() { /* ... */ };

    return Post;
  }]);

Если вы сделаете это, вам больше не придется вручную извлекать элементы из упакованного ответа, и каждый элемент будет экземпляром Post который имеет доступ к .foo метод. Вы можете просто написать:

<tr ng-repeat="post in posts" ng-class="{warning: post.foo()}">..</tr>

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

Это старый вопрос, но я сам столкнулся с этим вопросом. Решение Gargc - правильный подход, но есть улучшение. transformResponse принимает массив, который передается в $http оказание услуг. Вместо того, чтобы полностью заменить функцию преобразования, вы можете добавить свое преобразование к значениям по умолчанию, чтобы просто сделать необходимые обновления:

angular.module('foo')
    .factory('Post', function ($resource, $http) {
        var Post = $resource('/api/posts/:id', { id: '@id' }, {
            query: {
                method: 'GET',
                isArray: false,
                transformResponse: $http.defaults.transformResponse.concat(function(data, header) {
                    angular.forEach(data.items, function(item, idx) {
                        data.items[idx] = new Post(item);
                    });
                    return data;
                })
            }
        });

        Post.prototype.foo = function() { /* ... */ };

        return Post;
    });

Вы можете поместить метаданные в заголовок. Я всегда помещаю там данные подкачки. Таким образом, ваш запрос по-прежнему будет возвращать массив, что, я считаю, хорошо. Запросы должны возвращать массивы данных, а не отдельные данные.

Другие вопросы по тегам