AngularJS Data Models: $http VS $resource VS Restangular
Sauce AI for Test Authoring: Move from intent to execution in transactions.|xBack to ResourcesBlogPost
Sauce AI for Test Authoring: Move from intent to execution in transactions.
|
x
Sauce Labs software developerAlan Christopher Thomasand his squad feature been hard at employment updating our pot. He shared with us some insight into their dev operation, so we cogitate we & # x27; d show off what he & # x27; s done. Read his situation below.Over the preceding few month, the Sauce Labs web squad has fixed its crosshairs on several minute of our stack that needed to be refresh. One of those bits is the list of jobs all customers see when they firstly log into their account. It looks like this:

Our current app is built in Backbone.js. We vetted heap of options for frontend MVC frameworks and data binding that could supplant and simplify the existing Backbone code: Ember, Angular, React, Rivets, Stapes, etc. After mickle of enquiry, building some stuff, and personal preference, our team decided we were most comfy with Angular. We had one more thing we desire to verify, though, before settling.
How refine will it be to model our information?
This was the first question on most of our minds, and it was the one question about Angular that Google fly into a nullity of silence. Backbone has models and collections. Ember.js has Ember Data and ember-model. Stapes has extensible evident objects that can function as collections. But what about Angular? Most examples we found be extremely thin on the data bed, just returning simple JavaScript objects and portion them now to a $ scope model. So, we establish a small proof of concept using three different AngularJS information modeling techniques. This is a dumbed down version of our Jobs page, which only displays a leaning of jobs and their results. Our only basic requirement was that we kept business logic out of our controllers so they wouldn & # x27; t turn bloated. We gave ourselves some flexibility with the API reaction and allowed them to be wrapped with an object or not envelop to underscore the force of each approach. However, all cry requirelimit and fullparameters to be passed in the GET query string. Here & # x27; s what we wanted the leave DOM template to look like:
{{job.getResult ()}} | {{job.name}} |
Note that each resulting job should be capable to have a getResult () method that displays a human-readable outcome in a badge. The rendered page looks like this:

The Code: $ http vs $ imagination vs Restangular
So, here & # x27; s the resulting code for all three approaches, each implementing agetResult ()method on every job.
$http
In this approach, we created a service that get the API calls and wrapped each result as aJob()object with agetResult ()method defined on the prototype.API Response Format:
{
& quot; meta & quot;: {},
& quot; object & quot;: [
{
& quot; breakpointed & quot;: cipher,
& quot; browser & quot;: & quot; android & quot;,
& quot; browser_short_version & quot;: & quot; 4.3 & quot;,
...
},
{
...
},
...
]
}
models.js:
angular.module (& # x27; job.models & # x27;, [])
.service (& # x27; JobManager & # x27;, [& # x27; $ q & # x27;, & # x27; $ http & # x27;, & # x27; Job & # x27;, function ($ q, $ http, Job) {
return {
getAll: role (limit) {
var submit = $ q.defer ();
$ http.get (& # x27; /api/jobs? limit= & # x27; + boundary + & # x27; & amp; full=true & # x27;) .success (part (data) {
var jobs = [];
for (var i = 0; i & lt; data.objects.length; i ++) {
jobs.push (new Job (data.objects [i]));
}
deferred.resolve (jobs);
});
return deferred.promise;
}
};
}])
.factory (& # x27; Job & # x27;, function () {
function Job (data) {
for (attr in data) {
if (data.hasOwnProperty (attr))
this [attr] = data [attr];
}
}
Job.prototype.getResult = function () {
if (this.status == & # x27; accomplished & # x27;) {
if (this.passed === null) return & quot; Finished & quot;;
else if (this.passed === true) return & quot; Pass & quot;;
else if (this.passed === false) return & quot; Fail & quot;;
}
else return & quot; Running & quot;;
};
return Job;
});
controllers.js:
angular.module (& # x27; job.controllers & # x27;, [])
.controller (& # x27; jobsController & # x27;, [& # x27; $ scope & # x27;, & # x27; JobManager & # x27;, role ($ scope, JobManager) {
var limit = 20;
$ scope.loadJobs = part () {
JobManager.getAll (limit) .then (map (jobs) {
$ scope.jobs = job;
bound += 10;
});
};
$ scope.loadJobs ();
}]);
SUSA automates exploratory testing with persona-driven behavior, catching bugs that scripted automation misses.
This approach create for a passably unproblematic comptroller, but since we necessitate a custom method on the model, our services and factories quickly became verbose. Also, if we were to abstract away this behavior to apply to other data character (sub-accounts, burrow, etc.), we might end up writing a unhurt lot of boilerplate.
$resource
UPDATE: Per Micke & # x27; s suggestion in the comments section below, we & # x27; vepost a follow-upwith a cleaner execution of the $ resource variant of the Job model. It parse an API response similar to the one shown in the Restangular scenario and countenance for much cleaner method declaration utiliseangular.extend.
Angular provides its own$ resourcefulness factory, which has to be included in your project as a separate dependency. It conduct away some of the pain we felt in writing ourJobManagerservice boilerplate code and allows us to apply our custom method immediately to the$resourceprototype, then metamorphose responses to be enfold in itself.
API Response Format:
{
& quot; items & quot;: [
{
& quot; breakpointed & quot;: null,
& quot; browser & quot;: & quot; android & quot;,
& quot; browser_short_version & quot;: & quot; 4.3 & quot;,
...
},
{
...
}
...
]
}
models.js:
angular.module (& # x27; job.models & # x27;, [])
.factory (& # x27; Job & # x27;, [& # x27; $ imagination & # x27;, function ($ resourcefulness) {
var Job = $ resource (& # x27; /api/jobs/: jobId & # x27;, {total: & # x27; true & # x27;, jobId: & # x27; @ id & # x27;}, {
query: {
method: & # x27; GET & # x27;,
isArray: false,
transformResponse: function (data, cope) {
var wrapped = angular.fromJson (data);
angular.forEach (wrapped.items, map (item, idx) {
wrapped.items [idx] = new Job (item);
});
return wrapped;
}
}
});
Job.prototype.getResult = function () {
if (this.status == & # x27; consummate & # x27;) {
if (this.passed === zip) return & quot; Finished & quot;;
else if (this.passed === true) return & quot; Pass & quot;;
else if (this.passed === false) return & quot; Fail & quot;;
}
else return & quot; Running & quot;;
};
return Job;
}]);
controllers.js:
angular.module (& # x27; job.controllers & # x27;, [])
.controller (& # x27; jobsController & # x27;, [& # x27; $ scope & # x27;, & # x27; Job & # x27;, mapping ($ scope, Job) {
var boundary = 20;
$ scope.loadJobs = function () {
var jobs = Job.query ({limit: limit}, office (jobs) {
$ scope.jobs = jobs.items;
limit += 10;
});
};
$ scope.loadJobs ();
}]);
This approach likewise make for a pretty refined controller, except we really didn & # x27; t like that thequery()methodtook a callback instead of give us a hope didn & # x27; t return a promise directly, but afford us an object with the hope in a $ promise attribute(thanks Louis!). It felt moderately un-Angular a little ugly. Also, the process of transmute upshot objects and wrapping them felt like a strange dance to reach some simple doings(UPDATE: seethis post). We & # x27; d probably end up compose more boilerplate to abstract that part out.
Restangular
Last, but not least, we yieldRestangulara stroke. Restangular is a third-party library that attempts to abstract away pain point of dealing with API responses, reduce boilerplate, and do it in the well-nigh Angular-y way potential.
API Response Format:
[
{
& quot; breakpointed & quot;: null,
& quot; browser & quot;: & quot; android & quot;,
& quot; browser_short_version & quot;: & quot; 4.3 & quot;,
...
},
{
...
}
...
]
models.js:
angular.module (& # x27; job.models & # x27;, [])
.service (& # x27; Job & # x27;, [& # x27; Restangular & # x27;, function (Restangular) {
var Job = Restangular.service (& # x27; jobs & # x27;);
Restangular.extendModel (& # x27; occupation & # x27;, map (model) {
model.getResult = function () {
if (this.status == & # x27; accomplished & # x27;) {
if (this.passed === zero) return & quot; Finished & quot;;
else if (this.passed === true) return & quot; Pass & quot;;
else if (this.passed === false) return & quot; Fail & quot;;
}
else return & quot; Running & quot;;
};
return model;
});
homecoming Job;
}]);
controllers.js:
angular.module (& # x27; job.controllers & # x27;, [])
.controller (& # x27; jobsController & # x27;, [& # x27; $ scope & # x27;, & # x27; Job & # x27;, function ($ scope, Job) {
var bound = 20;
$ scope.loadJobs = map () {
Job.getList ({full: true, limit: limit}) .then (function (jobs) {
$ scope.jobs = jobs;
limit += 10;
});
};
$ scope.loadJobs ();
}]);
In this one, we got to cheat and useRestangular.service (), which provides all the RESTful goodies for us. It even abstracted away compose out full URLs for our API calls.Restangular.extendModel ()afford us an elegant way to attach method to each of our poser results, makinggetResult ()straightforward and clear. Lastly, the call in our accountant returns a promise! This let us write the control logic a bit more cleanly and allows us to be more flexible with the answer in the future.
tldr; Concluding Thoughts
Each of the three approach have their appropriate use cases, but I think in ours we & # x27; re leaning toward Restangular.
$http -$ http is built into Angular, so there & # x27; s no need for the extra overhead of loading in an external dependency. $ http is full for quick retrieval of server-side data that doesn & # x27; t really need any specific construction or complex behaviors. It & # x27; s probably best injected directly into your comptroller for simmpleness & # x27; s sake.
$ imagination -$ resouce is good for situations that are slimly more complex than $ http. It & # x27; s good when you have pretty structure data, but you plan to do most of your crunching, relationships, and other operations on the server side before delivering the API response. $ resource doesn & # x27; t let you do much once you get the data into your JavaScript app, so you should deliver it to the app in its final state and make more REST calls when you need to manipulate or seem at it from a different slant. Any custom demeanor on the guest side will need a lot of boilerplate.
Restangular -Restangular is a perfect option for complex operation on the client side. It lets you easily attach custom conduct and interact with your data in much the like way as former framework paradigms you & # x27; ve employ in the past. It & # x27; s promise-based, clean, and feature-rich. However, it might be overkill if your need are basic, and it carries along with it any extra import that come with bringing in additional third-party habituation.
Restangular appear to be adecently active project with the prospect of a 2.0that & # x27; s compatible with Angular 2.0, currently a private repository. However, a lot of the project & # x27; s progress seem to be dependent on the employment of a single developer for the time being.
We & # x27; re looking forward to seeing how Restangular progresses and whether or not it seems like a good fit for us at Sauce! If this blog post has piqued your sake and you & # x27; ll find as passionate about web development as we do experience free to check out here at Sauce or direct us a line.
Alan Christopher Thomas, Software Developer, Sauce Labs
Automate This With SUSA
Upload your APK or URL. SUSA explores like 10 real users — finds bugs, accessibility violations, and security issues. No scripts needed.
Try SUSA FreeTest Your App Autonomously
Upload your APK or URL. SUSA explores like 10 real users — finds bugs, accessibility violations, and security issues. No scripts.
Try SUSA Free