Trevor Ewen: In times past, we've dealt with more complicated interfaces for services that made it so we felt that it was best and most testable to transform our request at the point of service request.
A good example of this would be service providers that require formats that are frankly bad form or even bad naming conventions, possibly using strings like Y or N in the place of Booleans. These are things you don't want to be passing around your jobs from an app, and are much better transformed at the point of the service.
You can do this in a number of ways but probably the most friendly Angular way is the transformRequest property in the $http service. I'm going to give you a little look at that right here.
I have a simple app called Twitter Lite. What it does is, it calls to a passthrough API to send out tweets for me. I can say, "Hello, Angular fans," and then I have a comma separated list of autotags, things that should be automatically tagged to my tweet.
If we take a look at the network tab here, we've actually received a "400, bad request." The object we're sending up is comma separated autotags and the tweet, "Hello, Angular fans."
One problem is that Angular is not tagged in the tweet. We're either telling the service to do that or we're not doing it right. In the case for the error, we have an "error status is missing" right now because the service wants a parameter property called status on the object it's receiving from the post. So we're going to have to modify this request. We're going to do that through the transformRequest form.
What I have here is a simple Angular app. It's got a view controller right here that has a just the tweet and a the comma separated autotags. The view controller knows how to send up tweets. All it does is, it calls the API and passes this request, which has the tweet and the comma separated autotags. The API itself, all it's doing is passing in the tweet.
We need to disconnect this somewhere up here. It could be at the top of this method, although that's messier, a little less testable, not as good separation of concerns. In this case, Angular provides us this pattern, the transformer pattern. I define these in factories that simply return functions and receive data.
If you've seen my video on the response transformer, then you know that response transformers parse JSON strings back to JSON. A request transformer does the inverse, which stringifies data.
If we were to place this into the API right now -- let's say transformRequest -- it would be API request transformer. That's just like passing the function in there. This makes it really easy, really testable to use, and we don't have to do anything with the tweet in this service. Now we can do this.
This should behave the exact same way. If we were to refresh this and send up another tweet without the comma separated autotags, we're going to get the same thing, but we're also going to have a request that's still readable by the service.
Let's take a look at how we're going to build this guy out. What we're going to do is, we're going to use a TDD approach for building it. I've got some examples of requests and what we need to do with them in order to get this to work.
That's fine. Drop this down to the next line. With the comma separated autotags, we'll take transformer and tag request. This is going to give us the same kind of object, but it's going to automatically tag those things.
Let's take a look at this. The best way to do this is, grab this guy and then tag them. In Angular, we have JS and NG comp. We're going to make this fairly inflexible. We're going to want a fairly strict format for our tags.
Finally, I want to make sure this interface works fine when you haven't even specified comma separated autotags. If we say something along the lines of tweet. This is my tweet. I want it to still send out a valid status and not worry about it. This is not a use case in the current app, but if something else was calling this, it's good to plan ahead for these things with things like services, which are often called by a lot of parts of the application.
OK, let's take a look at this and run this real quick. We should have three specs failing. That makes sense because we haven't implemented any of this. Yeah, OK, great.
Let's take a look at the actual code here. The first thing we know is, we need this to be an object that is status equals the tweet. That should actually take care of two of our tests right there. It's just creating a new object out of the data.tweet. Yes, we are going to assume that there is a tweet there. You might want to code more protectively around this in the future because you can't always assume it. Maybe raise errors if there isn't a tweet.
Run the test. OK, we only have the issue with the comma separated autotags, which makes sense because we haven't done anything for this yet. This is going to be just a hair more complicated. The way I'm going to do this is, if data.comma-separated-autotags, then we are going to split them up.
We have an array, data.comma-autotags.split. We're going to split them in that strict definition that we had, the comma and space, so we've got each word, and then we're going to do a simple for loop. It's going to be array.length. We'll call this tag. We'll say data.tweet equals data.tweet, replace and tag with...There we go.
There are more elegant and foolproof ways to do this with regex expressions, which I think are definitely a good thing to check out. I'm just going for the easy route here. But if you were to do something like this, there might be a better way to go about it.
Let's try to run this again. It looks like we have all three tests passing. This should be automatically tagging everything we've got.
Let's see this guy in action now. Let's say we will have that "Hello, Angular fans of Egghead." Let's say Angular and Egghead. Clear that out, do a quick tweet. Not only did we get a 201, which means we got the right values. If we look at our status, it's tagged everything and it's added it to the status object.