Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
I am the chief technical officer at InVision App, Inc - a prototyping and collaboration platform for designers, built by designers. I also rock out in JavaScript and ColdFusion 24x7.
Meanwhile on Twitter
Loading latest tweet...
Ben Nadel at CFUNITED 2010 (Landsdown, VA) with:

Using The $http Service In AngularJS To Make AJAX Requests

By Ben Nadel on

When I first got into AngularJS, I started using the $resource service to communicate with the backend. This wasn't a decision that I made based on thoughtful analysis; rather, I was simply doing what I saw in various AngularJS tutorials. In retrospect, it was an unfortunate decision. There's nothing inherently wrong with $resource - it just provides a lot of ceremony and abstraction that I don't personally find valuable. As such, I wanted to take some time and start getting comfortable with using the $http service to make AJAX (Asynchronous JavaScript and XML) requests in my AngularJS applications.


 
 
 

 
  
 
 
 

NOTE: There are aspects of $resource that I do use and like. But, I think I'd rather try to reimplement those on top of $http instead of consuming the entire $resource module.

If you've used jQuery, then the $http service in AngularJS will seem familiar. While the details are different, it looks and behaves very much like the $.ajax() method in jQuery: it takes a request configuration and returns a promise. jQuery uses its own internal Deferred library while AngularJS uses a paired-down (and $digest-integrated) version of the "q" library.

In this, my frist exploration of the $http service in AngularJS, I wanted to make a really simple CRUD (Create, Read, Update, Delete) application. In fact, I'm not event allowing for "Update" as I felt that would do nothing but make the demo more complicated. So, in this demo, you can get a list of friends, add a friend, and delete a friend.

The $http service, in my code, is encapsulated within another service object - friendService. The storage mechanism for "friends" is intended to be hidden; so, you will see that I take special care not to let any of the $http artifacts leak out beyond the boundaries of the friendService. As responses come back from the server, I make sure to "unwrap" the payloads so that the friendService returns promises that deal only with the application data and never with the underlying transportation mechanism.

  • <!doctype html>
  • <html ng-app="Demo">
  • <head>
  • <meta charset="utf-8" />
  •  
  • <title>
  • Using The $http Service In AngularJS To Make AJAX Requests
  • </title>
  •  
  • <style type="text/css">
  •  
  • a[ ng-click ] {
  • color: #FF00CC ;
  • cursor: pointer ;
  • text-decoration: underline ;
  • }
  •  
  • </style>
  • </head>
  • <body ng-controller="DemoController">
  •  
  • <h1>
  • Using The $http Service In AngularJS To Make AJAX Requests
  • </h1>
  •  
  • <!-- Show existing friends. -->
  • <ul>
  • <li ng-repeat="friend in friends">
  •  
  • {{ friend.name }}
  •  
  • ( <a ng-click="removeFriend( friend )">delete</a> )
  •  
  • </li>
  • </ul>
  •  
  • <!-- Add a new friend to the list. -->
  • <form ng-submit="addFriend()">
  •  
  • <input type="text" ng-model="form.name" size="20" />
  • <input type="submit" value="Add Friend" />
  •  
  • </form>
  •  
  •  
  • <!-- Initialize scripts. -->
  • <script type="text/javascript" src="../../jquery/jquery-2.1.0.min.js"></script>
  • <script type="text/javascript" src="../../angularjs/angular-1.2.4.min.js"></script>
  • <script type="text/javascript">
  •  
  • // Define the module for our AngularJS application.
  • var app = angular.module( "Demo", [] );
  •  
  •  
  • // -------------------------------------------------- //
  • // -------------------------------------------------- //
  •  
  •  
  • // I control the main demo.
  • app.controller(
  • "DemoController",
  • function( $scope, friendService ) {
  •  
  • // I contain the list of friends to be rendered.
  • $scope.friends = [];
  •  
  • // I contain the ngModel values for form interaction.
  • $scope.form = {
  • name: ""
  • };
  •  
  • loadRemoteData();
  •  
  •  
  • // ---
  • // PUBLIC METHODS.
  • // ---
  •  
  •  
  • // I process the add-friend form.
  • $scope.addFriend = function() {
  •  
  • // If the data we provide is invalid, the promise will be rejected,
  • // at which point we can tell the user that something went wrong. In
  • // this case, I'm just logging to the console to keep things very
  • // simple for the demo.
  • friendService.addFriend( $scope.form.name )
  • .then(
  • loadRemoteData,
  • function( errorMessage ) {
  •  
  • console.warn( errorMessage );
  •  
  • }
  • )
  • ;
  •  
  • // Reset the form once values have been consumed.
  • $scope.form.name = "";
  •  
  • };
  •  
  •  
  • // I remove the given friend from the current collection.
  • $scope.removeFriend = function( friend ) {
  •  
  • // Rather than doing anything clever on the client-side, I'm just
  • // going to reload the remote data.
  • friendService.removeFriend( friend.id )
  • .then( loadRemoteData )
  • ;
  •  
  • };
  •  
  •  
  • // ---
  • // PRIVATE METHODS.
  • // ---
  •  
  •  
  • // I apply the remote data to the local scope.
  • function applyRemoteData( newFriends ) {
  •  
  • $scope.friends = newFriends;
  •  
  • }
  •  
  •  
  • // I load the remote data from the server.
  • function loadRemoteData() {
  •  
  • // The friendService returns a promise.
  • friendService.getFriends()
  • .then(
  • function( friends ) {
  •  
  • applyRemoteData( friends );
  •  
  • }
  • )
  • ;
  •  
  • }
  •  
  • }
  • );
  •  
  •  
  • // -------------------------------------------------- //
  • // -------------------------------------------------- //
  •  
  •  
  • // I act a repository for the remote friend collection.
  • app.service(
  • "friendService",
  • function( $http, $q ) {
  •  
  • // Return public API.
  • return({
  • addFriend: addFriend,
  • getFriends: getFriends,
  • removeFriend: removeFriend
  • });
  •  
  •  
  • // ---
  • // PUBLIC METHODS.
  • // ---
  •  
  •  
  • // I add a friend with the given name to the remote collection.
  • function addFriend( name ) {
  •  
  • var request = $http({
  • method: "post",
  • url: "api/index.cfm",
  • params: {
  • action: "add"
  • },
  • data: {
  • name: name
  • }
  • });
  •  
  • return( request.then( handleSuccess, handleError ) );
  •  
  • }
  •  
  •  
  • // I get all of the friends in the remote collection.
  • function getFriends() {
  •  
  • var request = $http({
  • method: "get",
  • url: "api/index.cfm",
  • params: {
  • action: "get"
  • }
  • });
  •  
  • return( request.then( handleSuccess, handleError ) );
  •  
  • }
  •  
  •  
  • // I remove the friend with the given ID from the remote collection.
  • function removeFriend( id ) {
  •  
  • var request = $http({
  • method: "delete",
  • url: "api/index.cfm",
  • params: {
  • action: "delete"
  • },
  • data: {
  • id: id
  • }
  • });
  •  
  • return( request.then( handleSuccess, handleError ) );
  •  
  • }
  •  
  •  
  • // ---
  • // PRIVATE METHODS.
  • // ---
  •  
  •  
  • // I transform the error response, unwrapping the application dta from
  • // the API response payload.
  • function handleError( response ) {
  •  
  • // The API response from the server should be returned in a
  • // nomralized format. However, if the request was not handled by the
  • // server (or what not handles properly - ex. server error), then we
  • // may have to normalize it on our end, as best we can.
  • if (
  • ! angular.isObject( response.data ) ||
  • ! response.data.message
  • ) {
  •  
  • return( $q.reject( "An unknown error occurred." ) );
  •  
  • }
  •  
  • // Otherwise, use expected error message.
  • return( $q.reject( response.data.message ) );
  •  
  • }
  •  
  •  
  • // I transform the successful response, unwrapping the application data
  • // from the API response payload.
  • function handleSuccess( response ) {
  •  
  • return( response.data );
  •  
  • }
  •  
  • }
  • );
  •  
  • </script>
  •  
  • </body>
  • </html>

There's nothing here that's too special. Again, this is more of a personal exploration of the $http service than it is a tutorial. That said, there are a few things going on that may be worth pointing out.

When I return my JSON (JavaScript Object Notation) from the server, I am prepending the response with the string value:

")]}',#chr( 10 )#"

... such that my data looks something like:

)]}',
[{"name":"Kim","id":1398096700363}]

This is done for security purposes. AngularJS will automatically strip out the prefix before it parses the JSON. This helps prevent malicious behaviors that use an authenticated JSON API as an attack vector.

I'm also using the .then() method on the $http promise instead of the .success() and .error() methods. I do this because neither .success() nor .error() return a new promise - they both return the original $http promise. By using .then(), I am able to return a new promise that unwraps (and normalizes) the API response.

The $http service can do a lot more than I have demonstrated (including caching and request / response transformations). But, as a first look at the service, it seems easy enough to use. More explorations to come.




Reader Comments

By the way, when you use $resource you get a resource object in the callbacks instead of you clean response object (it's an object decorated with properties that you may not want) and it will polute your database (or localstorage or else) if you store it as it is.
With $http you access the .data and get your clean object as it should be.
If you know any easy way to get a clean object with $resource I would really appreciate it :)

Reply to this Comment

Your Angular articles are the bomb! Also, I Tweeted simply because you have a popup that says "you rock the party that rocks the body" - badass and funny as hell.

Great work, and thanks!

Reply to this Comment

@Olivier,

We have that issue as well, which is one of the reasons I'd like to stop using $resource in the long run. In our app, we actually jump through a bunch of hoop since we attempt to merge both live data and *locally cached* data. We actually decorate the core Deferred object to allow the success callbacks to be invoked more than once (one time for locally cached data, if it exists, and one time for live data). I think somewhere in there we might "unwrap" the response and start passing *just* the data around; but, I don't remember exactly.

Reply to this Comment

@Ben,

that would be nice to gather all your last articles about $http services into one module and publish it on github :)

Reply to this Comment

Wow! So far I have not seen any better way of laying out the service "class".

Thank you for this gem!

Reply to this Comment

@Trailmax,

Glad you like it. I keep toying around with different approaches. The problem that I run into is that there are different class scenarios and different contexts that make various approaches more or less problematic

In this example, the class only gets instantiated once, so you can be less exacting in how you define things. What I mean is that if the class was meant to be instantiated multiple times, I'd try to use the "prototype" and then I'd have to use "this.xyz" to reference methods, which makes life a lot harder, but the code a bit more efficient (since the methods are all shared on the prototype).

Then there's the Controllers which use $scope to define some stuff, which is problematic because this approach (in this blog post) uses function "hoisting" which kinda of breaks when you start defining methods on $scope... which part of the reason people don't like to "leverage" hoisting like I do.

But, that said, I do like this layout as it most closely resembles the code I write on the server side.

Reply to this Comment

Once again Nader saved my day. This example made it really easy to understand the use of testable services. Thanks man!

Reply to this Comment

You can delete this comment as soon as it's read.

In this, my ?frist? exploration of the $http service in AngularJS, I wanted to make a really simple CRUD (Create, Read, Update, Delete) application. In fact, I'm not ?event? allowing for "Update" as I felt that would do nothing but make the demo more complicated.

Reply to this Comment

Hi Ben,

Great article! I have a simple question. I am trying to perform a simple http get request, however every time I make the request it doesn't perform like an Ajax request-instead it loads into the .php file. Do I have to format my .php a specific way in order to make the $http function asynchronously?

Cheers,
Spencer

Reply to this Comment

a nwebie quesstion: why the handleSuccess, handleError function recieve the response parameter but none is being sent when they are called?
i.e.
function handleError( response ) {

return( request.then( handleSuccess, handleError ) );

Reply to this Comment

If time allows it, I would love to hear if it is possible to load a $scope property with the JSON object retrieved by a successful then(). To be specific, how to unwrap the data and retain it in a $scope property in order to pass that very data (the JSON) to another piece of code.

Is it even possible such a thing?

I ask you this because from what I have experienced, the real unwraping is done in the view by the use of ng-repeat. But, I would love to learn how to retain the retrieved object in a $scope property or in a pure variable for the best.

Thank you very much

Reply to this Comment

I stand in awe looking at your clean service class layout, too. But I have to admit I do not understand how it works.

AFAIU, angular.module('app').service('MyService', constructorFunction) would instantiate an instance of the constructorFunction. But in your case, the constructor does *return* an object containing the public API?? Uh oh I cannot get my head around it.

Could you please drop a few words how this works?

Excellent article and your blog is a wonderful resource because you really strive for formal clarity and readability/maintainability. This is also the goal I am trying to achieve ...

Reply to this Comment

Regarding #chr( 10 )# being appended to your JSON server response, do you have an example, or a post on security related to this and other aspects. This is something I'm not to familiar with, or how to do, but I should be, so open to any resources that you've found useful and tips.

Reply to this Comment

Hi, i am trying to use this in a simple example. but i want to separate this in some files, how can be the structure to work?

can i put the service and error handler in one file and the first part in other?

thanks

Reply to this Comment

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.