httpi - A Lightweight $resource-Inspired Module For AngularJS
As I blogged about before, I'm not a huge fan of the $resource module in AngularJS. But, it does have some features that I do like; namely, URL interpolation and the encapsulation of URLs across requests to the same resource. I created the "httpi" module in an attempt to build these features on top of the $http service while still providing direct access to the flexibility of the $http service underneath.
View the httpi project on my GitHub account.
The httpi service is really just a preprocessor for the underlying $http service. It takes your configuration object, interpolates the URL (hence the "i" in "httpi"), and then passes the updated configuration object off to $http. It then returns the same promise that the $http service returned to it.
The httpi service can also create $resource-inspired objects that apply the same URL across different HTTP calls. But, like the httpi service, each HttpiResource instance method takes a normal configuration object, modifies it, and then passes it off to the underlying $http service.
NOTE: When I say "$resource-inspired," I do so very loosely. I do not mean to imply that I am recreating the $resource feature-set; rather, that I am extracting what I personally found useful in the $resource-oriented approach.
To see this in action, take a look at the code below (which is the example on my GitHub project page). I'm creating an httpi resource and then invoking several of the convenience methods:
<!doctype html>
<html ng-app="Demo">
<head>
<meta charset="utf-8" />
<title>
Using The httpi Service To Make AJAX Requests In AngularJS
</title>
</head>
<body ng-controller="DemoController">
<h1>
Using The httpi Service To Make AJAX Requests In AngularJS
</h1>
<!-- Initialize scripts. -->
<script type="text/javascript" src="vendor/angular-1.2.16.min.js"></script>
<script type="text/javascript" src="../lib/httpi.js"></script>
<script type="text/javascript">
// Define the module for our AngularJS application.
var app = angular.module( "Demo", [ "httpi" ] );
// -------------------------------------------------- //
// -------------------------------------------------- //
// I control the main demo.
app.controller(
"DemoController",
function( $scope, httpi ) {
console.warn( "None of the API endpoints exist - they will all throw 404." );
// NOTE: The (.|.) notation will be stripped out automatically; it's only
// here to improve readability of the "happy paths" for interpolation
// labels. The following urls are pre-processed to be identical:
// --
// api/friends/( :listCommand | :id/:itemCommand )
// api/friends/:listCommand:id/:itemCommand
var resource = httpi.resource( "api/friends/( :listCommand | :id/:itemCommand )" );
// Clear list of friends - matching listCommand.
resource.post({
data: {
listCommand: "reset"
}
});
// Create a new friend - no matching URL parameters.
resource.post({
data: {
name: "Tricia"
}
});
// Get a given friend - ID matching.
resource.get({
data: {
id: 4
}
});
// Make best friend - ID, itemCommand matching.
resource.post({
data: {
id: 4,
itemCommand: "make-best-friend"
}
});
// Get gets friends - no matching URL parameters.
resource.get({
params: {
limit: "besties"
}
});
// Get a friend as a JSONP request.
// --
// NOTE: The "resource" will auto-inject the "JSON_CALLBACK" marker that
// AngularJS will automatically replace with an internal callback name.
resource.jsonp({
data: {
id: 43
}
});
}
);
</script>
</body>
</html>
Notice that the configuration objects passed into the "resource" methods don't have to include the Method or URL properties - these are automatically interpolated and injected into the configuration object before they are passed-off to the underlying $http service.
When we run the above code, we get the following network activity:
POST /api/friends/reset
POST /api/friends
GET /api/friends/4
POST /api/friends/4/make-best-friend
GET /api/friends?limit=besties
GET /api/friends/43?callback=angular.callbacks._0
Of course, you don't have to use the resource part of the module; the resource is kind of like a preprocessor for the httpi service which is, itself, a preprocessor for the $http service. If you call the httpi service directly, you'll still get the URL interpolation - you simply have to pass-in the URL with each request.
Obviously, the value of this module is heavily colored by my own experience with the $resource module in AngularJS. If you're loving $resource, I am not suggesting that you stop using it. But, for me personally, it wasn't a huge value-add. So, I tried to take the parts that I did like and rebuild them on top of the $http service in a very transparent way.
Want to use code from this post? Check out the license.
Reader Comments
Couldn't this be written into a decorator in order to avoid having to inject the wrapping service instead of the original $http?
@Michael,
You probably could. I'm only recently learning about decorators; so, at the time I wrote this, it wasn't an option for me personally.
I just created a gist showing how this could be done in case anyone is interested https://gist.github.com/mxa055/324e77a2dd20fb4aa227.
I usually prefer using decorators as they provide a cleaner solution (in my opinion) and can be applied at any point of the development cycle without requiring major refactoring of the existing code.
@Michael,
I tried to take a look in your implementation as decorator but the link is dead, perhaps you can update it. Thanks !
@Charles,
there you go https://gist.github.com/masimakopoulos/324e77a2dd20fb4aa227#file-gistfile1-ts