Note that there are some explanatory texts on larger screens.

plurals
  1. POAngularJS and complex JSON returned by django tastypie
    text
    copied!<p>I have few resources written on AngularJS that access a Tastypie API. Everything works fine, except for a detail: tastypie always encapsulate the actual result inside a <code>objects</code> attribute on a JSON, example:</p> <p><code>/api/v1/reminder/</code>:</p> <pre><code>{ 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" } } </code></pre> <p>It was wasy to fix using a callback to the <code>get()</code> call:</p> <pre><code>Reminder.get().$then(function (result) { $scope.reminders = result.data.objects; }); </code></pre> <p>But I know <code>result.resource</code> is an actual <code>Reminder</code> instance.</p> <pre><code>.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; }]) </code></pre> <p>Now I need to implement behavior on my <code>Reminder</code> class, and I need every element on my <code>meta.objects</code> to be an instance of <code>Reminder</code>:</p> <pre><code>Reminder.get().$then(function (result) { $scope.reminders = result.data.objects; result.resource.TESTE(); // -&gt; outputs 'asd' o = result.data.objects[0]; o.TESTE // -&gt; undefined, obvisously i = new Reminder(o); i.TESTE() // -&gt; outputs 'asd' }); </code></pre> <p>So, how to I get angularjs to understand that every object on <code>objects</code> is the actual result so it behaves like a list of instances?</p> <p>The workaround is to creating a new list iterating on the results creating the instances, but it's not optimal...</p> <p>Suggestions?</p> <h1>Solution by @rtcherry:</h1> <p>As suggested by rtcherry, I used <a href="https://github.com/mgonto/restangular" rel="noreferrer">restangular</a></p> <p>Configuring the reading of request data:</p> <pre><code>.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; }); }]) </code></pre> <p>Loading the reminders:</p> <pre><code>function RemindersCtrl ($scope, $rootScope, Reminder) { $scope.reminders = Reminder.getList(); } </code></pre> <p>Adding my custom method to <code>Reminder</code> (not as clean as ngResource, but doable):</p> <pre><code>.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; }]) </code></pre> <h1>Solution by @moderndegree:</h1> <p>I used pure <code>ngResource</code>:</p> <pre><code>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; }]) </code></pre> <p>My controller:</p> <pre><code>Transaction.query(filter).$then(function (result) { $scope.days = []; var transactions = result.resource; resource[0].remainingDays(); // it works }); </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload