AngularJS From Beginning: REST API - Part Eight

I am here to continue the discussion around AngularJS. Today, we will discuss about the REST API with $http service with AngularJS. Also in case you have not had a look at our previous articles of this series, go through the following links:

In this article, I will discuss how AngularJS supports working with RESTful web services.

Representational State Transfer (REST) is a style of API that operates over HTTP requests. The requested URL identifies the data to be operated on, and the HTTP method identifies the operation that is to be performed. REST is a style of API rather than a formal specification, and there is a lot of debate and disagreement about what is and isn’t RESTful, a term used to indicate an API that follows the REST style. AngularJS is pretty flexible about how RESTful web services are consumed. You should use the services that I describe in this article when you are performing data operations on a RESTful API. You may initially prefer to use the $http service to make Ajax requests, especially if you are coming from a jQuery background. To that end, I describe the use of $http at the start of the article, before explaining its limitations when used with REST and the advantages of using the $resource service as an alternative. For this, we first need to create a RESTful web API.

REST Services

REST (REpresentational State Transfer) services allow for a separation of concerns.REST services are not concerned with the user interface or user state, and clients that use REST services are not concerned with data storage or business logic. Clients can be developed independently of the REST services, as we have shown in previous chapters, using mock data. REST services can likewise be developed independently of the client, with no concern for client specifics or even the types of clients using the services. REST services should perform in the same way for all clients. REST services should be stateless. A REST service should never hold data in a session

variable. All information needed for a REST service call should be contained in the request and header passed from the client to the service. Any state should be held in the client and not in the service. There are many ways to hold state in an AngularJS application, including local storage, cookies, or cache storage. A REST web service is said to be RESTful when it adheres to the following constrants:

  • It’s URL-based (e.g., http://www.micbutton.com/rs/blogPost). 

  • It uses an Internet media type such as JSON for data interchange. 

  • It uses standard HTTP methods (GET, PUT, POST, DELETE).

HTTP methods have a particular purpose when used with REST services. The following is the standard way that HTTP methods should be used with REST services:

  1. POST should be used to:
    1. Create a new resources.
    2. Retrieve a list of resources when a large amount of request data is required to be passed to the service.
  2. PUT should be used to update a resource.
  3. GET should be used to retrieve a resource or a list of resources.
  4. DELETE should be used to delete a resource.
For doing this, we first create a model class with the below mention members :-
 
 Name Type Required
 name String Yes   
 category string yes
 price number Yes
 
Now we need to write down a REST Api with the post, get and delete method.
 
Now, come back to angular js and add the below mentioned html and js files for the program - 
 
http_restapi.html
  1. <!DOCTYPE html>  
  2. <html xmlns="http://www.w3.org/1999/xhtml" ng-app="TestApp">  
  3. <head>  
  4.     <title>Access REST Api using $http</title>  
  5.     <script src="angular.js"></script>  
  6.     <link href="bootstrap.css" rel="stylesheet" />  
  7.     <link href="bootstrap-theme.css" rel="stylesheet" />  
  8.     <script src="http_restapi.js"></script>  
  9. </head>  
  10. <body ng-controller="mainController">  
  11.     <div class="panel panel-primary">  
  12.         <h3 class="panel-heading">Products</h3>  
  13.         <ng-include src="'tableView.html'" ng-show="displayMode == 'list'"></ng-include>  
  14.         <ng-include src="'editorView.html'" ng-show="displayMode == 'edit'"></ng-include>  
  15.     </div>  
  16. </body>  
  17. </html>  
tableview.html
  1. <div class="panel-body">  
  2.     <table class="table table-striped table-bordered">  
  3.         <thead>  
  4.             <tr>  
  5.                 <th>Name</th>  
  6.                 <th>Category</th>  
  7.                 <th class="text-right">Price</th>  
  8.                 <th></th>  
  9.             </tr>  
  10.         </thead>  
  11.         <tbody>  
  12.             <tr ng-repeat="item in products">  
  13.                 <td>{{item.name}}</td>  
  14.                 <td>{{item.category}}</td>  
  15.                 <td class="text-right">{{item.price | currency}}</td>  
  16.                 <td class="text-center">  
  17.                     <button class="btn btn-xs btn-primary"  
  18.                             ng-click="deleteProduct(item)">  
  19.                         Delete  
  20.                     </button>  
  21.                     <button class="btn btn-xs btn-primary" ng-click="editOrCreateProduct(item)">  
  22.                         Edit  
  23.                     </button>  
  24.                 </td>  
  25.             </tr>  
  26.         </tbody>  
  27.     </table>  
  28.     <div>  
  29.         <button class="btn btn-primary" ng-click="listProducts()">Refresh</button>  
  30.         <button class="btn btn-primary" ng-click="editOrCreateProduct()">New</button>  
  31.     </div>  
  32. </div>  
editorview.html
  1. <div class="panel-body">  
  2.     <div class="form-group">  
  3.         <label>Name:</label>  
  4.         <input class="form-control" ng-model="currentProduct.name" />  
  5.     </div>  
  6.     <div class="form-group">  
  7.         <label>Category:</label>  
  8.         <input class="form-control" ng-model="currentProduct.category" />  
  9.     </div>  
  10.     <div class="form-group">  
  11.         <label>Price:</label>  
  12.         <input class="form-control" ng-model="currentProduct.price" />  
  13.     </div>  
  14.     <button class="btn btn-primary" ng-click="saveEdit(currentProduct)">Save</button>  
  15.     <button class="btn btn-primary" ng-click="cancelEdit()">Cancel</button>  
  16. </div>  
app.js
  1. var testApp = angular.module('TestApp', []);  
http_restapi.js
  1. testApp.controller("mainController"function ($scope) {  
  2.     $scope.displayMode = "list";  
  3.   
  4.     $scope.currentProduct = null;  
  5.   
  6.     $scope.listProducts = function () {  
  7.         $scope.products = [  
  8.         { id: 0, name: "Dummy1", category: "Test", price: 1.25 },  
  9.         { id: 1, name: "Dummy2", category: "Test", price: 2.45 },  
  10.         { id: 2, name: "Dummy3", category: "Test", price: 4.25 }];  
  11.     }  
  12.   
  13.     $scope.deleteProduct = function (product) {  
  14.         $scope.products.splice($scope.products.indexOf(product), 1);  
  15.     }  
  16.   
  17.     $scope.createProduct = function (product) {  
  18.         $scope.products.push(product);  
  19.         $scope.displayMode = "list";  
  20.     }  
  21.   
  22.     $scope.updateProduct = function (product) {  
  23.         for (var i = 0; i < $scope.products.length; i++) {  
  24.             if ($scope.products[i].id == product.id) {  
  25.                 $scope.products[i] = product;  
  26.                 break;  
  27.             }  
  28.         }  
  29.         $scope.displayMode = "list";  
  30.     }  
  31.   
  32.     $scope.editOrCreateProduct = function (product) {  
  33.         $scope.currentProduct = product ? angular.copy(product) : {};  
  34.         $scope.displayMode = "edit";  
  35.     }  
  36.   
  37.     $scope.saveEdit = function (product) {  
  38.         if (angular.isDefined(product.id)) {  
  39.             $scope.updateProduct(product);  
  40.         } else {  
  41.             $scope.createProduct(product);  
  42.         }  
  43.     }  
  44.   
  45.     $scope.cancelEdit = function () {  
  46.         $scope.currentProduct = {};  
  47.         $scope.displayMode = "list";  
  48.     }  
  49.     $scope.listProducts();  
  50. });  
Now, in the above angular js controller file at first I will not use  $http service. Now run the program and the output will be as below - 
 
 
 
Now, I will change the crud related method with $http service method to access  the restapi data.
  1. testApp  
  2.     .constant("baseUrl""http://localhost:1021/products/")  
  3.     .controller("mainController"function ($scope, $http, baseUrl) {  
  4.         $scope.displayMode = "list";  
  5.   
  6.         $scope.currentProduct = null;  
  7.   
  8.         $scope.listProducts = function () {  
  9.             $http.get(baseUrl).success(function (data) {  
  10.                 $scope.products = data;  
  11.             });  
  12.         }  
  13.   
  14.         $scope.deleteProduct = function (product) {  
  15.             $http({  
  16.                 method: "DELETE",  
  17.                 url: baseUrl + product.id  
  18.             }).success(function () {  
  19.                 $scope.products.splice($scope.products.indexOf(product), 1);  
  20.             });  
  21.         }  
  22.   
  23.         $scope.createProduct = function (product) {  
  24.             $http.post(baseUrl, product).success(function (newProduct) {  
  25.                 $scope.products.push(newProduct);  
  26.                 $scope.displayMode = "list";  
  27.             });  
  28.         }  
  29.   
  30.         $scope.updateProduct = function (product) {  
  31.             $http({  
  32.                 url: baseUrl + product.id,  
  33.                 method: "PUT",  
  34.                 data: product  
  35.             }).success(function (modifiedProduct) {  
  36.                 for (var i = 0; i < $scope.products.length; i++) {  
  37.                     if ($scope.products[i].id == modifiedProduct.id) {  
  38.                         $scope.products[i] = modifiedProduct;  
  39.                         break;  
  40.                     }  
  41.                 }  
  42.                 $scope.displayMode = "list";  
  43.             });  
  44.         }  
  45.   
  46.         $scope.editOrCreateProduct = function (product) {  
  47.             $scope.currentProduct = product ? angular.copy(product) : {};  
  48.             $scope.displayMode = "edit";  
  49.         }  
  50.   
  51.         $scope.saveEdit = function (product) {  
  52.             if (angular.isDefined(product.id)) {  
  53.                 $scope.updateProduct(product);  
  54.             } else {  
  55.                 $scope.createProduct(product);  
  56.             }  
  57.         }  
  58.   
  59.         $scope.cancelEdit = function () {  
  60.             $scope.currentProduct = {};  
  61.             $scope.displayMode = "list";  
  62.         }  
  63.         $scope.listProducts();  
  64.     });  

Installing the ngResource Module

The $resource service is defined within an optional module called ngResource that must be downloaded into the angularjs folder. Go to http://angularjs.org, click Download, select the version you want and download the file. Download the angular-resource.js file into the angularjs folder.

Configuring $resource Service

The first thing I have to do is set up the $resource service so that it knows how to work with the RESTful Deployd service. Here is the statement that does this:

  1. $scope.productsResource = $resource(baseUrl + ":id", { id: "@id" });  

The $resource service object is a function that is used to describe the URLs that are used to consume the RESTful service. The URL segments that change per object are prefixed with a colon (the : character). For the first argument I combine the value of the baseUrl constant with :id to indicate a URL segment that will change. The second argument is a configuration object whose properties specify where the value for the variable segment will come from. Each property must correspond to a variable segment from the first argument, and the value can be fixed or, as I have done in this example, bound to a property on the data object by prefixing a property name with the @ character. The result from calling the $resource service function is an access object that can be used to query and modify the server data using the methods that I have described in below.

Modifying Data Objects

The query method populates the collection array with Resource objects, which defines all of the properties specified in the data returned by the server and some methods that allow manipulation of the data without needing to use the collections array. The  below tables describe the methods that Resource objects define.

Name

Descriptions

$delete()

Deletes the object from the server; equivalent to calling $remove()

$get()

Refreshes the object from the server, clearing any uncommitted local changes

$remove()

Deletes the object from the server; equivalent to calling $delete()

$save()

$save()

Deleting Data Objects

The $delete and $remove methods generate the same requests to the server and are identical in every way. The wrinkle in their use is that they send the request to remove an object from the server but don’t remove the object from the collection array. This is a sensible approach, since the outcome of the request to the server isn’t known until the response is received and the application will be out of sync with the server if the local copy is deleted and the request subsequently returns an error.

Configuring the $resource Service Actions

The get, save, query, remove, and delete methods that are available on the collection array and the $-prefixed equivalents on individual Resource objects are known as actions. By default, the $resource service defines the actions. The $resource service object function can be invoked with a third argument that defines actions. The actions are expressed as object properties whose names correspond to the action that is being defined, or redefined, since you can replace the default actions if need be. Each action property is set to a configuration object. I have used only one property, method, which sets the HTTP method used for the action. The effect of my change is that I have defined a new action called create, which uses the POST method, and I have redefined the save action so that it uses the PUT method. The result is to make the actions supported by the productsResoures access object more consistent with the Deployd API, separating the requests for creating new objects from those that modify existing objects.

Name

Descriptions

method

Sets the HTTP method that will be used for the Ajax request.

params

Specifies values for the segment variables in the URL passed as the first argument to the $resource

service function.

url

Overrides the default URL for this action.

isArray

When true, specifies that the response will be a JSON data array. The default value, false, specifies that

the response to the request will be, at most, one object.

 
Now I again the change the angular js controller file for implement the $resource objects. 
  1. testApp  
  2.     .constant("baseUrl""http://localhost:1021/products/")  
  3.     .controller("mainController"function ($scope, $http, $resource, baseUrl) {  
  4.         $scope.displayMode = "list";  
  5.   
  6.         $scope.currentProduct = null;  
  7.   
  8.         $scope.productsResource = $resource(baseUrl + ":id", { id: "@id" });  
  9.   
  10.         $scope.listProducts = function () {  
  11.             $scope.products = $scope.productsResource.query();  
  12.         }  
  13.   
  14.         $scope.deleteProduct = function (product) {  
  15.             product.$delete().then(function () {  
  16.                 $scope.products.splice($scope.products.indexOf(product), 1);  
  17.             });  
  18.             $scope.displayMode = "list";  
  19.         }  
  20.   
  21.         $scope.createProduct = function (product) {  
  22.             new $scope.productsResource(product).$save().then(function (newProduct) {  
  23.                 $scope.products.push(newProduct);  
  24.                 $scope.displayMode = "list";  
  25.             });  
  26.         }  
  27.   
  28.         $scope.updateProduct = function (product) {  
  29.             product.$save();  
  30.             $scope.displayMode = "list";  
  31.         }  
  32.   
  33.         $scope.editOrCreateProduct = function (product) {  
  34.             $scope.currentProduct = product ? product : {};  
  35.             $scope.displayMode = "edit";  
  36.         }  
  37.   
  38.         $scope.saveEdit = function (product) {  
  39.             if (angular.isDefined(product.id)) {  
  40.                 $scope.updateProduct(product);  
  41.             } else {  
  42.                 $scope.createProduct(product);  
  43.             }  
  44.         }  
  45.   
  46.         $scope.cancelEdit = function () {  
  47.             if ($scope.currentProduct && $scope.currentProduct.$get) {  
  48.                 $scope.currentProduct.$get();  
  49.             }  
  50.             $scope.currentProduct = {};  
  51.             $scope.displayMode = "list";  
  52.         }  
  53.         $scope.listProducts();  
  54.     });