【问题标题】:AngularJS and complex JSON returned by django tastypieAngularJS 和复杂的 JSON 由 django sweetpie 返回
【发布时间】:2013-06-22 23:52:58
【问题描述】:

我在 AngularJS 上编写的访问 Tastypie API 的资源很少。一切正常,除了一个细节:tastepie 总是将实际结果封装在 JSON 的 objects 属性中,例如:

/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;
}])

@moderndegree 的解决方案:

我用的是纯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

});

【问题讨论】:

  • 你可能想试试restangular
  • 哇,看起来不错..我会研究一下
  • 完成。它不像向 Angular 资源中添加方法那么容易,但可以做到。但是 restangular 的许多有用功能得到了回报。你想从你的评论中做出回答,以便我标记它吗?
  • 嘿,我是 Restangular 的创建者。如果你必须选择,你想要哪一个更好的 ngResource 或 Restangular 来满足你的所有使用需求?为什么?感谢您提供使 Restangular 变得更好的信息 :)
  • @moderndegree 的解决方案是否适用于 1.2.12?我正在尝试返回对象列表并包含一个元元素以保留 total_pages (以避免为此提出另一个请求)。似乎即使我在示例中完全相同地转换响应,但稍后在返回的对象中元数据不可用。

标签: angularjs angularjs-resource


【解决方案1】:

我最终做了以下操作,以便直接在Reminder.query() 返回的结果上保留meta 对象(扩展来自@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 对象也可用。

【讨论】:

    【解决方案2】:

    如果您想避免使用额外的库,您应该能够执行以下操作:

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

    这会将您的转换附加到$HttpProvider 的默认转换器。

    注意:如果我在这方面有错误,请纠正我,但我相信此功能需要 v1.1.2 或更高版本。

    【讨论】:

    • +1 用于发布使用 AngularJS 1.1.x 分支中较新的ngResource 功能的解决方案。
    • 我要试试!我在restangular没有给我发帖请求的响应时遇到一些麻烦,它在ngresource上工作。恢复到后者可以节省我一些时间。
    • 酷。限制您必须加载的库的数量总是好的。另外,这是我最近发布的一个答案,它解决了类似的问题。 stackoverflow.com/questions/17332922/… @Shaman 正在寻找一种在默认转换之前转换响应的方法。
    • 它有效...我必须将meta 保存为objects 数组上的属性以供以后使用。我现在将找到一种方法在我的所有资源上设置它(但是不是全球性的)
    【解决方案3】:

    您可能想尝试类似restangular

    需要进行一些配置才能使其正常工作。一个例子是here

    【讨论】:

      猜你喜欢
      • 2011-10-24
      • 2013-07-11
      • 2012-06-04
      • 1970-01-01
      • 2013-01-01
      • 2012-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-06-12
      相关资源
      最近更新 更多