Enter Your Email Address to Watch This Lesson

Your link to unlock this lesson will be sent to this email address.

Unlock this lesson and all 986 of the free egghead.io lessons, plus get Angular 1.x content delivered directly to your inbox!



Existing egghead members will not see this. Sign in.

transformResponse

4:01 Angular 1.x lesson by

In his debut lesson, Trevor test drives the transformation of a response from an external REST service using the the transformResponse option of AngularJS's $http service.

Get the Code Now
click to level up

egghead.io comment guidelines

Avatar
egghead.io

In his debut lesson, Trevor test drives the transformation of a response from an external REST service using the the transformResponse option of AngularJS's $http service.

Avatar
Magnus

Good lesson! It would be helpful to also have the karma.conf.js file listed. I'm having trouble getting the modules to be loaded in the spec whether I run it in WebStorm or from the command line for this example. I have added all the JS files in the karma.conf.js script.

In reply to egghead.io
Avatar
Joel

Here's the karma.conf.js:

// Karma configuration
// Generated on Sun Oct 27 2013 16:05:49 GMT-0400 (EDT)

module.exports = function (config) {
   config.set({

       // base path, that will be used to resolve files and exclude
       basePath: '',


       // frameworks to use
       frameworks: ['jasmine'],


       // list of files / patterns to load in the browser
       files: [
           'bower_components/jquery/jquery.js',
           'bower_components/angular/angular.js',
           'bower_components/angular-mocks/angular-mocks.js',
           'bower_components/angular-resource/angular-resource.js',
           'bower_components/lodash/dist/lodash.compat.js',
           'bower_components/node-uuid/uuid.js',
           'bower_components/neosavvy-javascript-core/neosavvy-javascript-core.js',
           'bower_components/neosavvy-javascript-angular-core/neosavvy-javascript-angular-core.js',
           'app/public-api.js',
           'app/**/*.js',
           'app/**/*.html',
           'test/**/*.js'
       ],


       // list of files to exclude
       exclude: [

       ],

       preprocessors: {
           'app/**/*.html': ['ng-html2js']
       },

       ngHtml2JsPreprocessor: {
           moduleName: 'templates'
       },

       // test results reporter to use
       // possible values: 'dots', 'progress', 'junit', 'growl', 'coverage'
       reporters: ['progress'],


       // web server port
       port: 9876,


       // enable / disable colors in the output (reporters and logs)
       colors: true,


       // level of logging
       // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
       logLevel: config.LOG_INFO,


       // enable / disable watching file and executing tests whenever any file changes
       autoWatch: true,


       // Start these browsers, currently available:
       // - Chrome
       // - ChromeCanary
       // - Firefox
       // - Opera (has to be installed with `npm install karma-opera-launcher`)
       // - Safari (only Mac; has to be installed with `npm install karma-safari-launcher`)
       // - PhantomJS
       // - IE (only Windows; has to be installed with `npm install karma-ie-launcher`)
       browsers: ['PhantomJS'],


       // If browser does not capture in given timeout [ms], kill it
       captureTimeout: 60000,


       // Continuous Integration mode
       // if true, it capture browsers, run tests and exit
       singleRun: false
   });
};
In reply to Magnus
Avatar
Rob

I am new to Angular so I wanted to see if this response still brings back the full object or just what you request? I had someone tell me that Angular is not able to parse the object before being sent back. Is this true? I wanted to find out because I don't want to have to make a request and get the full object back if I don't need it all before then parsing. Hope that makes sense.

In reply to egghead.io
Avatar
Leo

I'm hoping you can clarify something for me.

I'm new at this and I'm just trying to get my head wrapped around it. I'm thinking that when you inject ["$http"], you put the argument as ($http). Why do you inject the transformer into the service using the name services.transformers.ApiResponse and then put the argument as apiResourceTransformer. Why are the names different?

I'm sure this is elementary, so please kindly help this 1st grader out.

Thanks,
Leo

In reply to egghead.io
Avatar
Ignacio Morales

Can you please share the full example project of this video. It will help a lot to have an overview of this.

Thanks!

Trevor Ewen: An often underutilized AngularJS pattern is the transformResponse pattern. transformResponse is built into the $http service. What it allows you to do is transform the exact service response to whatever kind of object, maybe throw errors, or provide some login for the response itself, and it's built in.

This provides a really testable way to make sure your service interfaces are performing the exact way you want to work with them in the application.

I have a simple app here. It's called "Public API." It has a view with a controller right on top of it. All this view does is it loops over these repos. It's a GitHub service we're going to be calling. It says, "Give me the repo.userName and the repo.gitURL."

If we look at the controller, the controller's just making the service request and setting the results right here. When we look at the actual service, this is the meat right here.

In this case, we have a get request and then the URL, standard HTTP stuff. We defined a success handler here, with the promise API.

Let's take a look at the screen here. In this case, we refresh it. It's making the service request, but it's not finding those things. The big reason for that is that the actual objects returned in this case are much more complex than we want. They have a load of properties in them.

We want this giturl, but we want it CamelCase. For that user name property, we're actually looking at this fullname. We want to have this conform to the interface that we desire and the interface on the screen.

We want to keep these objects simple. An easy way to do that is through a response transformer. A response transformer is going to be defined like so. It's going to be injected into the service. We'll call it "apiResponseTransformer." I had that in the queue because I knew that's what I was going to name it.

We attach it to the service like this, transformReponse. Then we pass it in. This can be defined as a simple function, closure inside here or whatever you want.

In this case, I do want to assign it as a separate factory so that we can test it easier and it conforms to Angular patterns. We'll add it to the publicapi.transformers module. That's called "ApiResponse."

This is a simple factory. All it's doing is returning a function that takes in data and parses the JSON for data. What I want to do is take a test-driven approach to how we're going to write this guy out.

Let's cut those in half. Take a look at the two pieces. The reason this has to do a JSON parse...Normally, Angular does that for you. But in the case of user-defined transformResponse, it actually just gives you the direct JSON string. It'll let you do whatever you want with it, in case you want to parse it differently, parse it conditionally, or do replaces in the string.

In this case, this is one valid case that if we receive an empty array string, we should return just an empty array parse. It should return an empty array when it receives an empty array.

The spec here is real simple. All it's doing is it's putting the transformer up here. It gives us a copy of that. Then we're able to just call directly into that input/output kind of stuff, which is essentially what these transformers are.

If the empty array there, then it should equal that. If we try and run these tests right now...All right. Looks like we're good. That's a pretty easy case.

In this case, we want to add that...Let's see what it is...the userName and gitURL. In this case, we're actually going to use some live data that we got from the server. It should parse the response to three items with userName and gitURL defined. I cheated a little bit right here. The response is rather large. I've got it declared in the before method.

That comes directly from the Chrome Inspector. I've got my items right here, so I can actually just take this guy real quick. If we just say, "expect equal transformer." We'll open this guy up, too. This is going to be the value.

This is what we're going to transform to. The response itself is the object we're getting back from the service. Format that up real quick.

When we run this test, it should fail. That's because we don't have any of that defined. All it's doing is parsing the JSON in this case.

Let's take a look at what we'd have to do there. In this case, I want to say, "data = JSON.parse(data)." Let's return data so the first test is still valid. Then, "if data.length." Got an array with some elements. Let's map it, using the _.map function, just because I like the syntax. It's going to be data, and then we add function, which is a repo.

We're just going to return these objects. We're going to say, "userName" is the "repo.full_name," and "gitURL" is the "repo.gitURL."

Right here, what we're doing is we're very cleanly defining the JSON object in the way we want to return. This will provide some benefit to us in working within the app. It'll also show up on our index page.

We'll see if that test passes. We've got that. We've got that all defined right here. Let's take a look at the [inaudible 05:37] live page.

Here we go. Right from the service, we have our userName right here and then our gitURL right here, transformed in the exact interface that we want.

HEY, QUICK QUESTION!
Joel's Head
Why are we asking?