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 git_url, but we want it CamelCase. For that user name property, we're actually looking at this full_name. 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.