AngularJS и сложный JSON, возвращенный django deliciouspie

У меня есть несколько ресурсов, написанных на AngularJS, которые обращаются к Tastypie API. Все работает нормально, за исключением одной детали: deliciouspie всегда инкапсулирует фактический результат внутри атрибута objects в JSON, например:

/api/v1/reminder/:

{
    meta: {
        limit: 20,
        next: null,
        offset: 0,
        previous: null,
        total_count: 3
    },
    objects: [{
        category: {
            color: "#999999",
            id: 1,
            name: "Groceries",
            resource_uri: "/api/v1/category/1"
        },
        description: "",
        due_date: "2010-10-16",
        id: 1,
        repeat: "weekly",
        resource_uri: "/api/v1/reminder/1",
        value: "-50"
    }, {
        category: {
            color: "#999999",
            id: 1,
            name: "Groceries",
            resource_uri: "/api/v1/category/1"
        },
        description: "",
        due_date: "2010-10-17",
        id: 2,
        repeat: "weekly",
        resource_uri: "/api/v1/reminder/2",
        value: "-50"
    }
}

Было неудобно исправлять использование обратного вызова для вызова get():

Reminder.get().$then(function (result) {
    $scope.reminders  = result.data.objects;
});

Но я знаю, что result.resource — это реальный экземпляр Reminder.

.factory('Reminder', ['$resource', function($resource){
    var Reminder = $resource('/api/v1/reminder/:id', {}, {
        get: {
            method: 'GET',
            isArray: false
        }
    });

    Reminder.prototype.TESTE = function () {console.log('asd');};

    return Reminder;
}])

Теперь мне нужно реализовать поведение в моем классе Reminder, и мне нужно, чтобы каждый элемент моего meta.objects был экземпляром Reminder:

Reminder.get().$then(function (result) {
    $scope.reminders  = result.data.objects;

    result.resource.TESTE(); // -> outputs 'asd'

    o = result.data.objects[0];
    o.TESTE // -> undefined, obvisously
    i = new Reminder(o);
    i.TESTE() // -> outputs 'asd'
});

Итак, как мне заставить angularjs понять, что каждый объект на objects является фактическим результатом, поэтому он ведет себя как список экземпляров?

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

Предложения?

Решение от @rtcherry:

Как предложил rtcherry, я использовал restangular.

Настройка чтения данных запроса:

.config(['RestangularProvider', function(RestangularProvider) {
    RestangularProvider.setBaseUrl("/api/v1");

    RestangularProvider.setResponseExtractor(function(response, operation, what, url) {
        var newResponse;
        if (operation === "getList") {
            newResponse = response.objects;
            newResponse.metadata = response.meta;
        } else {
            newResponse = response.data;
        }
        return newResponse;
    });
}])

Загрузка напоминаний:

function RemindersCtrl ($scope, $rootScope, Reminder) {
    $scope.reminders = Reminder.getList();
}

Добавляю свой пользовательский метод в Reminder (не такой чистый, как ngResource, но выполнимый):

.factory('Reminder', ['Restangular', '$filter', function(Restangular, $filter){
    var Reminder = Restangular.all('reminder');

    var remainingDays = function () {
        //do stuff
    };

    // adding custom behavior
    Restangular.addElementTransformer('reminder', false, function (reminder) {
        reminder.remainingDays = remainingDays;
        return reminder;
    });

    return Reminder;
}])

Решение от @moderngrade:

Я использовал чистый ngResource:

var tastypieDataTransformer = function ($http) {
    return $http.defaults.transformResponse.concat([
        function (data, headersGetter) {
            var result = data.objects;
            result.meta = data.meta;
            return result;
        }
    ])
};

...

.factory('Reminder', ['$resource', '$http', function($resource, $http){
    var Reminder = $resource('/api/v1/reminder/:id', {}, {
        query: {
            method: 'GET',
            isArray: true,
            transformResponse: tastypieDataTransformer($http)
        }
    });

    Reminder.prototype.remainingDays = function () {
        // doing stuff
    };

    return Reminder;
}])

Мой контроллер:

Transaction.query(filter).$then(function (result) {
    $scope.days = [];
    var transactions = result.resource;
    resource[0].remainingDays(); // it works

});

person Marcio Cruz    schedule 26.06.2013    source источник
comment
Вы можете попробовать что-то вроде restangular.   -  person rtcherry    schedule 26.06.2013
comment
ой, вроде неплохо.. посмотрю   -  person Marcio Cruz    schedule 26.06.2013
comment
сделано. это не так просто, как добавить метод к ресурсу angular, но это можно сделать... но restangular окупается благодаря многим полезным функциям. Вы хотите сделать ответ из своего комментария, чтобы я мог его отметить?   -  person Marcio Cruz    schedule 26.06.2013
comment
Привет, я создатель Restangular. Если вам нужно выбрать, какой из них лучше ngResource или Restangular для всего вашего использования? И почему? Спасибо за информацию, чтобы сделать Restangular лучше :)   -  person mgonto    schedule 29.06.2013
comment
Работает ли решение @moderndegree в 1.2.12? Я пытаюсь вернуть список объектов и включить метаэлемент для сохранения total_pages (чтобы избежать еще одного запроса только для этого). Кажется, даже несмотря на то, что я преобразовываю ответ точно так же, как в примере, метаданные позже недоступны в возвращаемом объекте.   -  person mdrozdziel    schedule 11.02.2014


Ответы (3)


Если вы хотите избежать использования дополнительной библиотеки, вы должны сделать следующее:

$resource('/api/v1/reminder/', {}, {
    query: {
        method: 'GET',
        isArray: true,
        transformResponse: $http.defaults.transformResponse.concat([
            function (data, headersGetter) {
                return data.objects;
            }
        ])
    }
});

Это добавит ваше преобразование к преобразователю по умолчанию $HttpProvider.

Примечание. Исправьте меня, если я ошибаюсь, но я считаю, что для этой функции требуется версия 1.1.2 или выше.

person Brian Lewis    schedule 27.06.2013
comment
+1 за публикацию решения, в котором используются новые функции ngResource из ветки 1.1.x AngularJS. - person rtcherry; 27.06.2013
comment
я собираюсь попробовать! у меня возникли проблемы с тем, что restangular не дает мне ответа на почтовый запрос, и это сработало на ngresource. возвращение к более позднему сэкономит мне время. - person Marcio Cruz; 28.06.2013
comment
Прохладно. Всегда полезно ограничить количество загружаемых библиотек. Кроме того, вот ответ, который я недавно опубликовал, который решил аналогичную проблему. stackoverflow.com/questions/17332922/ @Shaman искал способ преобразовать ответ перед преобразованием по умолчанию. - person Brian Lewis; 28.06.2013
comment
это работает... Мне пришлось сохранить meta как свойство в массиве objects для последующего использования. Теперь я найду способ установить это на всех моих ресурсах (но не глобально) - person Marcio Cruz; 28.06.2013

Вы можете попробовать что-то вроде restangular.

Для этой работы требуется некоторая конфигурация. Например, здесь.

person rtcherry    schedule 26.06.2013

В итоге я сделал следующее, чтобы сохранить объект meta непосредственно в результатах, возвращаемых Reminder.query() (расширяя ответ от @moderndegree).

var Reminder = $resource('/api/v1/reminder/', {}, {
  query: {
    method: 'GET',
    isArray: true,
    transformResponse: $http.defaults.transformResponse.concat([
      function (data, headersGetter) {
          return data.objects;
      }
    ]),
    interceptor: {
      response: function(response) {
        response.resource.meta = response.data.meta;
      }
    }
  }
});

Это позволяет вам получить значение meta непосредственно в возвращаемом объекте:

var reminders = Reminder.query(...);
console.log(reminders.meta); // Returns `meta` as expected.

Я думаю, что также можно было бы сделать что-то подобное внутри обратного вызова из Reminder.query, поскольку там также доступен объект response.

person Evan Stern    schedule 03.10.2014