Skip to main content
Ben Nadel at CFUNITED 2010 (Landsdown, VA) with: Sandy Clark
Ben Nadel at CFUNITED 2010 (Landsdown, VA) with: Sandy Clark ( @sandraclarktw )

Using The $http Service In AngularJS To Make AJAX Requests

By 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.

Want to use code from this post? Check out the license.

Reader Comments

7 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 :)

1 Comments

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!

15,674 Comments

@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.

15,674 Comments

@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.

2 Comments

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.

2 Comments

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

1 Comments

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 ) );

1 Comments

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

1 Comments

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 ...

3 Comments

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.

1 Comments

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

1 Comments

I'm also wondering the same things as @hmejias + as for data: i want to send a name like in your example, but also an age: can you point me in the right direction for doing this?

1 Comments

Hi Ben, I like what you are doing here and would like to implement it with amfphp.

This is what works for me before using your methods:

function load_categories() {
var obj = {
"cat_search": "all"
};
var callData = JSON.stringify({
"serviceName": "api/items",
"methodName": "get_categories",
"parameters": [obj]
});
$.post("http://000.000.000.000/Amfphp/?contentType=application/json", callData, categoriesSuccess);

function categoriesSuccess(data) {
console.log("Data: "+data.result);
}

I have tried a number of various implementations of your code but with no luck. Errors include not being able to use the $.post function to not receiving post data on the server.

Do you have any suggestions?
Thanks in advance for any leads,

Andros

2 Comments

Hi ,

I am getting a issue in http request with patch method.below is my code:
$http({
url: 'https://apistage.dealsignal.com/api/v0/company_watchlists/'+wishlist_id,
method: 'PATCH',
params: {list:{add_company_ids:['61737'],name:wishlist_name},api_key: api_key}
})
.success(function (response) {
console.log(response);
}).
error(function (response) {
console.log(response);
return false;
});

Same request with pathc method works in rest client on chrome.

1 Comments

Hello,
good article! I did not understand tough why do you wrap the returning public interface in parenthesis :\

return({
addFriend: addFriend,
getFriends: getFriends,
removeFriend: removeFriend
});

3 Comments

@Indio,

You are right that they both do the same - make ajax calls to the server.
But ajax call can be anything, this is a valid ajax request POST /server/getAllMyFatCats?me=1234
REST services have a more "strict" standard, so the above call will be something like GET /server/user/1234/cats?type=fat (user 1234 is me).
And you can do something like POST /server/user/1234/cats to add a new cat to user 1234 [or you can do /server/cats with a user property value of 1234).
So $http is very "free-spirited" in what it lets you do. $Resource is geared better towards the above standard where you can define one route and get all the REST methods (GET, POST, PUT, DELETE).
To make this response complete I also have to mention a library called RETANGULAR https://github.com/mgonto/restangular which is even a more advanced version of $resource.
What should I use? - if you are using APIS that are well defined according to the standard, go ahead and use $resource or Restangular. If your calls are not conforming to the standard - better use plain $http.
Hope it helps!

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel