Adding Custom Methods to Data Models with Angular $resource

Posted by Bill McGee in JavaScript

Sauce Labs software developer Alan Christopher Thomas and his team have been hard at work updating our stack. He shared with us some insight into their revised dev process, so we thought we’d show off what he’s done. Read his follow-up post below. Thanks for your great feedback to this post. Previously we examined three different approaches to modeling data in AngularJS. We've since incorporated some of your feedback, so we wanted to share that information here. You can also see updates we made in our original post. One of our commenters made mention of a cleaner approach to adding custom methods to $resource models when our API response response allows it, using angular.extend(). In this implementation, we're imagining an API response that looks like this:

[
  {
    "breakpointed": null,
    "browser": "android",
    "browser_short_version": "4.3",
    ...
  },
  {
    ...
  }
  ...
]

Each of the response objects in the list is a "Job" that contains a whole lot of metadata about an individual job that's been run in the Sauce cloud. We want to be able to iterate over the jobs to build a list for our users, showing the outcome of each: "Pass," "Fail," etc. Our template looks something like this:

{{ job.getResult() }} {{ job.name }}

Note the job.getResult() call. In order to get this convenience, however, we need to be able to attach a getResult() method to each Job returned in the response. So, here's what the model looks like, using Angular $resource:

angular.module('job.models', [])
    .factory('Job', ['$resource', function($resource) {
        var Job = $resource('/api/jobs/:jobId', {
            full: 'true',
            jobId: '@id'
        });
    angular.extend(Job.prototype, {
        getResult: function() {
            if (this.status == 'complete') {
                if (this.passed === null) return "Finished";
                else if (this.passed === true) return "Pass";
                else if (this.passed === false) return "Fail";
            }
            else return "Running";
        }
    });

    return Job;
}]);

Note that since each resulting object returned by $resource is a Job object itself, we can simply extend Job.prototype to include the behavior we want for every individual job instance. Then, our controller looks like this (revised from the original post to make use of the not-so-obvious promise):

angular.module('job.controllers', [])
    .controller('jobsController', ['$scope', '$http', 'Job', function($scope, $http, Job) {
        $scope.loadJobs = function() {
            $scope.isLoading = true;
            var jobs = Job.query().$promise.then(function(jobs) {
                $scope.jobs = jobs;
            });
        };
    $scope.loadJobs();
}]);

The simplicity of this example makes $resource a much more attractive option for our team's data-modeling needs, especially considering that for simple applications, custom behavior isn't incredibly unwieldy to implement. Alan Christopher Thomas, Software Developer, Sauce Labs

Discuss: Adding Custom Methods to Data Models with Angular $resource
0 Comments

Free Trial

Get access to a free 14-day trial version, or contact Sales for more information.