AngularJS From Beginning: Http Request or Ajax - Part Seven

I am here to continue the discussion around AngularJS. Today, we will discuss about the $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, we will discuss about the built-in AngularJS service for making ajax request and representing asynchronous activities.

Why to use Ajax Services?

Ajax is the foundation of the modern web application, and you will use the services that I describe in this article every time that you need to communicate with a server without causing the browser to load new content and, in doing so, dump your AngularJS application. That said, if you are consuming data from a RESTful API, then you should use the $resource service. I will describe REST and $resource in the next article, but the short version is that $resource provides a higher-level API that is built on the services I describe in this article and makes it easier to perform common data operations.

Making Ajax Requests

The $http service is used to make and process Ajax requests, which are standard HTTP requests that are performed asynchronously. Ajax is at the heart of modern web applications, and the ability to request content and data in the background while the user interacts with the rest of the application is an important way of creating a rich user experience. There are two ways to make a request using the $http service. The first—and most common—is to use one of the convenience methods that the service defines, which I have described in below table and which allows you to make requests using the most commonly needed HTTP methods. All of these methods accept an optional configuration object, which I describe in the “Configuring Ajax Requests” section later in this article.

Name

Descriptions

get(url, config)

Performs a GET request for the specified URL.

post(url, data, config)

Performs a POST request to the specified URL to submit the specified data.

delete(url, config)

Performs a DELETE request to the specified URL.

put(url, data, config)

Performs a PUT request with the specified data and URL.

patch(url, data, config)

Performs a PATCH request with the specified data and URL.

head(url, config)

Performs a HEAD request to the specified URL.

jsonp(url, config)

Performs a GET request to obtain a fragment of JavaScript code that is then executed. JSONP, which stands for JSON with Padding, is a way of working around the limitations that browsers apply to where JavaScript code can be loaded from.

The other way to make an Ajax request is to treat the $http service object as a function and pass in a configuration object. This is useful when you require one of the HTTP methods for which there is not a convenience method available. You pass in a configuration object that includes the HTTP method you want to use. I’ll discuss about how to make Ajax requests in this way in the next article where I discuss about RESTful services, but in this article I am going to focus on the convenience methods.

Receiving Ajax Responses

Making a request is only the first part of the Ajax process, and I also have to receive the response when it is ready. The A in Ajax stands for asynchronous, which means that the request is performed in the background, and you will be notified when a response from the server is received at some point in the future. AngularJS uses a JavaScript pattern called promises to represent the result from an asynchronous operation, such as an Ajax request. A promise is an object that defines methods that you can use to register functions that will be invoked when the operation is complete. The promise objects returned from the $http methods in above table define the methods shown in below.

Name

Descriptions

success(fn)

Invokes the specified function when the HTTP request has successfully completed

error(fn)

Invokes the specified function when the request does not complete successfully

then (fn,fn)

Registers a success function and an error function

The success and error methods pass their functions a simplified view of the response from the server. The success function is passed the data that the server sends, and the error function is passed a string that describes the problem that occurred. Further, if the response from the server is JSON data, then AngularJS will parse the JSON to create JavaScript objects and pass them to the success function automatically.

Getting More Response Details

Using the then method on the promise object allows you to register both a success and error function in a single method call. But also, more importantly, it provides access to more detailed information about the response from the server. The object that the then method passes to its success and error functions defines the properties I have described in below table.

Name

Descriptions

data

Returns the data from the request

status

Returns the HTTP status code returned by the server

headers

Returns a function that can be used to obtain headers by name

config

The configuration object used to make the request (see the “Configuring Ajax Requests” section for details)

For demonstrate the above concept, we first create a json data and then load those data using ajax request.

data.json

[
    {
        "Category": "ASP.NET",
        "Book": "Mastering ASP.Net 5",
        "Publishers": "Wrox Publication",
        "Price": 500.00
    },
    {
        "Category": "MVC",
        "Book": "Professional MVC 5",
        "Publishers": "Wrox Publication",
        "Price": 750.00
    },
    {
        "Category": "JQuery",
        "Book": "ABCD of JQuery",
        "Publishers": "BPB Publication",
        "Price": 500.00
    }
]

App.js

var testApp = angular.module('TestApp', []);

Ajax.html

<!DOCTYPE html>
<html ng-app="TestApp">
<head>
    <title>AngularJS Ajax</title>
    <script src="angular.js"></script>
    <link href="../../RefStyle/bootstrap.min.css" rel="stylesheet" />
    <script src="app.js"></script>
    <script src="ajax.js"></script>
</head>
<body ng-controller="ajaxController">
    <div class="panel panel-default">
        <div class="panel-body">
            <table class="table table-striped table-bordered">
                <thead>
                    <tr>
                        <th>Name</th>
                        <th>Category</th>
                        <th>Price</th>
                    </tr>
                </thead>
                <tbody>
                    <tr ng-hide="products.length">
                        <td colspan="4" class="text-center">No Data</td>
                    </tr>
                    <tr ng-repeat="item in products">
                        <td>{{item.Category}}</td>
                        <td>{{item.Book}}</td>
                        <td>{{item.Publishers}}</td>
                        <td>{{item.price | currency}}</td>
                    </tr>
                </tbody>
            </table>
            <p>
                <button class="btn btn-primary" ng-click="loadData()">
                    Load Data
                </button>
                <button class="btn btn-primary" ng-click="loadDataPromise()">
                    Load Data With Promise
                </button>
            </p>
        </div>
    </div>
</body>
</html>

Ajax.js

testApp.controller("ajaxController", function($scope, $http) {
    
    $scope.loadData = function() {
        $http.get("data.json").success(function(data) {
            $scope.products = data;
        });
    };

    $scope.loadDataPromise = function() {
        $http.get("data.json").then(function(response) {
            console.log("Status: " + response.status);
            console.log("Type: " + response.headers("content-type"));
            console.log("Length: " + response.headers("content-length"));
            $scope.products = response.data;
        });
    };
});

The output of the above code is.

Processing Other Data Types

Although obtaining JSON data is the most common use for the $http service, you may not always have the luxury of working with a data format that AngularJS will process automatically. If this is the case, then AngularJS will pass the success function an object containing the properties discussed above and you will be responsible for parsing the data. To give you a simple example of how this works, I created a simple XML file called productData.xml that contains the same product information as the previous example, but expressed as a fragment of XML.

Data.xml

<?xml version="1.0" encoding="UTF-8"?>
<products>
    <product Category="NodeJs" Book="Beginning of NodeJs" Publishers="BPB Publication" Price="500" />
    <product Category="ASP.Net" Book="Mastering ASP.Net 4" Publishers="Wrox Publication" Price="700" />
    <product Category="MVC" Book="Professional MVC 6" Publishers="Wrox Publication" Price="500" />
</products>

Now add another button in the ajax.html file for load the data from xml file and change the ajax.js file code as below.

testApp.controller("ajaxController", function($scope, $http) {
    
    $scope.loadData = function() {
        $http.get("data.json").success(function(data) {
            $scope.products = data;
        });
    };

    $scope.loadDataPromise = function() {
        $http.get("data.json").then(function(response) {
            console.log("Status: " + response.status);
            console.log("Type: " + response.headers("content-type"));
            console.log("Length: " + response.headers("content-length"));
            $scope.products = response.data;
        });
    };

    $scope.loadXMLData = function() {
        $http.get("data.xml").then(function(response) {
            $scope.products = [];
            var productElems = angular.element(response.data.trim()).find("product");
            for (var i = 0; i < productElems.length; i++) {
                var product = productElems.eq(i);
                $scope.products.push({
                    Category: product.attr("Category"),
                    Book: product.attr("Book"),
                    Publishers: product.attr("Publishers"),
                    price: product.attr("price")
                });
            }
        });
    };
});

Configuring Ajax Requests

The methods defined by the $http service all accept an optional argument of an object containing configuration settings. For most applications, the default configuration used for Ajax requests will be fine, but you can adjust the way the requests are made by defining properties on the configuration object corresponding to below table.

Name

Descriptions

data

Sets the data sent to the server. If you set this to an object, AngularJS will serialize it to the JSON format.

headers

Used to set request headers. Set headers to an object with properties whose names and values correspond to the headers and values you want to add to the request.

method

Sets the HTTP method used for the request.

params

Used to set the URL parameters. Set params to an object whose property names and values correspond to the parameters you want to include.

timeout

Specifies the number of milliseconds before the request expires. transformRequest Used to manipulate the request before it is sent to the server

transformResponse

Used to manipulate the response when it arrives from the server

url

Sets the URL for the request.

withCredentials

When set to true, the withCredentials option on the underlying browser request object is enabled, which includes authentication cookies in the request.

The most interesting configuration feature is the ability to transform the request and response through the aptly named transformRequest and transformResponse properties. AngularJS defines two built-in transformations; outgoing data is serialized into JSON, and incoming JSON data is parsed into JavaScript objects.

Html file code

<!DOCTYPE html>
<html ng-app="TestApp">
<head>
    <title>AngularJS Ajax</title>
    <script src="angular.js"></script>
    <link href="../../RefStyle/bootstrap.min.css" rel="stylesheet" />
    <script src="app.js"></script>
    <script src="ajax_config.js"></script>
</head>
<body ng-controller="ajaxController">
    <div class="panel panel-default">
        <div class="panel-body">
            <table class="table table-striped table-bordered">
                <thead>
                    <tr>
                        <th>Name</th>
                        <th>Category</th>
                        <th>Price</th>
                    </tr>
                </thead>
                <tbody>
                    <tr ng-hide="products.length">
                        <td colspan="4" class="text-center">No Data</td>
                    </tr>
                    <tr ng-repeat="item in products">
                        <td>{{item.Category}}</td>
                        <td>{{item.Book}}</td>
                        <td>{{item.Publishers}}</td>
                        <td>{{item.price | currency}}</td>
                    </tr>
                </tbody>
            </table>
            <p>
                <button class="btn btn-primary" ng-click="loadData()">
                    Load Data
                </button>
                <button class="btn btn-primary" ng-click="loadXMLData()">
                    Load Data (XML)
                </button>
            </p>
        </div>
    </div>
</body>
</html>

AngularJS file code

testApp.controller("ajaxController", function($scope, $http) {

    $scope.loadData = function() {
        $http.get("data.json").success(function(data) {
            $scope.products = data;
        });
    };

    $scope.loadXMLData = function() {
        var config = {
            transformResponse: function(data, headers) {
                if ((headers("content-type") === "application/xml" || headers("content-type") === "text/xml") && angular.isString(data)) {
                    var products = [];
                    var productElems = angular.element(data.trim()).find("product");
                    for (var i = 0; i < productElems.length; i++) {
                        var product = productElems.eq(i);
                        products.push({
                            Category: product.attr("Category"),
                            Book: product.attr("Book"),
                            Publishers: product.attr("Publishers"),
                            price: product.attr("price")
                        });
                    }
                    return products;
                } else {
                    return data;
                }
            }
        };
        $http.get("data.xml", config).success(function(data) {
            $scope.products = data;
        });
    };

});

Transforming a Request

You can transform a request by assigning a function to the transformRequest property of the configuration object. The function is passed the data that will be sent to the server and a function that returns header values (although many headers will be set by the browser just before it makes the request). The result returned by the function will be used for the request, which provides the means for serializing data.

Html file code

<!DOCTYPE html>
<html ng-app="TestApp">
<head>
    <title>AngularJS Ajax</title>
    <script src="angular.js"></script>
    <link href="../../RefStyle/bootstrap.min.css" rel="stylesheet" />
    <script src="app.js"></script>
    <script src="ajax_transforms.js"></script>
</head>
<body ng-controller="ajaxController">
    <div class="panel panel-default">
        <div class="panel-body">
            <table class="table table-striped table-bordered">
                <thead>
                    <tr>
                        <th>Name</th>
                        <th>Category</th>
                        <th>Price</th>
                    </tr>
                </thead>
                <tbody>
                    <tr ng-hide="products.length">
                        <td colspan="4" class="text-center">No Data</td>
                    </tr>
                    <tr ng-repeat="item in products">
                        <td>{{item.Category}}</td>
                        <td>{{item.Book}}</td>
                        <td>{{item.Publishers}}</td>
                        <td>{{item.price | currency}}</td>
                    </tr>
                </tbody>
            </table>
            <p>
                <button class="btn btn-primary" ng-click="loadData()">
                    Load Data
                </button>
                <button class="btn btn-primary" ng-click="sendData()">
                    Send Data
                </button>
            </p>
        </div>
    </div>
</body>
</html>

Angularjs file code

testApp.controller("ajaxController", function($scope, $http) {

    $scope.loadData = function() {
        $http.get("data.json").success(function(data) {
            $scope.products = data;
        });
    };

    $scope.sendData = function() {
        var config = {
            headers: {
                "content-type": "application/xml"
            },
            transformRequest: function(data, headers) {
                var rootElem = angular.element("<xml>");
                for (var i = 0; i < data.length; i++) {
                    var prodElem = angular.element("<product>");
                    prodElem.attr("Category", data[i].Category);
                    prodElem.attr("Book", data[i].Book);
                    prodElem.attr("Publishers", data[i].Publishers);
                    rootElem.append(prodElem);
                }
                rootElem.children().wrap("<products>");
                return rootElem.html();
            }
        };
        $http.post("ajax_transforms.html", $scope.products, config);
    };

});

The output of the code is as below.

Working with Promises

Promises are a way of registering interest in something that will happen in the future, such as the response sent from a server for an Ajax request. Promises are not unique to AngularJS, and they can be found in many different libraries, including jQuery, but there are variations between implementations to accommodate differences in design philosophy or the preferences of the library developers. There are two objects required for a promise: a promise object, which is used to receive notifications about the future outcome, and a deferred object, which is used to send the notifications. For most purposes, the easiest way to think of promises is to regard them as a specialized kind of event; the deferred object is used to send events via the promise objects about the outcome of some task or activity. AngularJS provides the $q service for obtaining and managing promises, which it does through the methods that I have described in below table. In the sections that follow, I’ll show you how the $q service works as I build out the example application.

Name

Descriptions

all(promises)

Returns a promise that is resolved when all of the promises in the specified array are resolved or any of them is rejected

defer()

Creates a deferred object

reject(reason)

Returns a promise that is always rejected

when(value)

Wraps a value in a promise that is always resolved (with the specified value as a result)

We can define the deferred object with the help of $q.defer method and a deferred object defines the methods as below.

Name

Descriptions

resolve(result)

Signals that the deferred activity has completed with the specified value

reject(reason)

Signals that the deferred activity has failed or will not be completed for the specified reason

notify(result)

Provides an interim result from the deferred activity

promise

Returns a promise object that receives the signals from the other methods

The main pattern of use is to get a deferred object and then call the resolve or reject method to signal the outcome of the activity.

The promise objects defines the below methods,

Name

Descriptions

then(success, error, notify)

Registers functions that are invoked in response to the deferred object’s resolve, reject, and notify methods. The functions are passed the arguments that were used to call the deferred object’s methods.

catch(error)

Registers just an error handling function, which is passed the argument used to call the deferred object’s reject method.

finally(fn)

Registers a function that is invoked irrespective of the promise being resolved or rejected. The function is passed the argument used to call the deferred object’s resolve or reject method.

Promise.html

<!DOCTYPE html>
<html ng-app="TestApp">
<head>
    <title>AngularJS Ajax Promise</title>
    <script src="angular.js"></script>
    <link href="../../RefStyle/bootstrap.min.css" rel="stylesheet" />
    <script src="app.js"></script>
    <script src="promise.js"></script>
</head>
<body ng-controller="ajaxController">
    <div class="well" promise-worker>
        <button class="btn btn-primary">Heads</button>
        <button class="btn btn-primary">Tails</button>
        <button class="btn btn-primary">Abort</button>
        Outcome: <span promise-observer></span>
    </div>
</body>
</html>

Promise.js

testApp.directive("promiseWorker", function($q) {
    var deferred = $q.defer();
    
    return {
        link: function(scope, element, attrs) {
            element.find("button").on("click", function(event) {
                var buttonText = event.target.innerText;
                
                if (buttonText == "Abort") {
                    deferred.reject("Aborted");
                } else {
                    deferred.resolve(buttonText);
                }
            });
        },
        controller: function($scope, $element, $attrs) {
            this.promise = deferred.promise;
        }
    };
});

testApp.directive("promiseObserver", function() {
    return {
        require: "^promiseWorker",
        link: function(scope, element, attrs, ctrl) {
            ctrl.promise.then(function(result) {
                element.text(result);
            }, function(reason) {
                element.text("Fail (" + reason + ")");
            });
        }
    };
});

testApp.controller("ajaxController", function($scope, $http) {
    // Controller logic can be added here
});

The output of the controller is as below.

 


Similar Articles