In Chapter 6, I put all of the features in place for displaying the product data to the user, but I did so using dummy data so that I could focus on building the basic plumbing of the application. It is now time to switch over to using the real data, which I will obtain from the Deployd server that I set up right at the start of Chapter 6.
AngularJS provides support for making Ajax requests through a service called $http. I describe how services work in detail in Part 3 and the $http service itself in Chapter 23, but you can get a sense of how it works through the changes I made to the top-level sportsStoreCtrl controller, as shown in Listing 7-1.
Listing 7-1. Making an Ajax Request in the sportsStore.js File
angular.module(“sportsStore”)
.constant(“dataUrl”, “http://localhost:5500/products”)
.controller(“sportsStoreCtrl”, function ($scope, $http, dataUrl) {
$scope.data = {};
$http.get(dataUrl)
.success(function (data) {
$scope.data.products = data;
})
.error(function (error) {
$scope.data.error = error;
});
});
Most JavaScript methods calls, including those made on AngularJS components, are synchronous, which means that execution doesn’t move on to the next statement until the current one has been completed. That doesn’t work when making network requests in web applications because we want the user to be able to interact with the application while the request is being made in the background.
I am going to obtain the data I need using an Ajax request. Ajax stands for Asynchronous JavaScript and XML, where the important word is asynchronous. An Ajax request is a regular HTTP request that happens asynchronously, in other words, in the background. AngularJS represents asynchronous operations using promises, which will be familiar to you if you have used libraries such as jQuery (and which I introduced in Chapter 5 and explain in detail in Chapter 20).
The $http service defines methods for making different kinds of Ajax request. The get method, which is the one I have used here, uses the HTTP GET method to request the URL passed as an argument. I have defined the URL as a constant called dataUrl and used the URL from Chapter 6 with which I tested the Deployd server.
The $http.get method starts the Ajax request, and execution of the application continues, even though the request has yet to be completed. AngularJS needs a way to notify me when the server has responded to the request, which is where the promise comes in. The $http.get method returns an object that defines success and error methods. I pass functions to these methods, and AngularJS promises to call one of them to tell me how the request turns out.
AngularJS will invoke the function I passed to the success method if everything with the HTTP request went well and—as a bonus—will automatically convert JSON data to JavaScript objects and pass them as the argument to the success function. If there is a problem with the Ajax HTTP request, then AngularJS will invoke the function I passed to the error method.
■ Tip JSON stands for JavaScript Object Notation and is a data exchange format that is widely used in web applications. JSON represents data in a way that is similar to JavaScript, which makes it easy to operate on JSON data in JavaScript applications. JSON has largely displaced XML, the X in Ajax, because it is human-readable and easy to implement. I introduced JSON in Chapter 5, and you can learn about the details of it at http://en.wikipedia.org/wiki/Tson.
The success function I have used in the listing is simple because it relies on the automatic conversion that AngularJS performs for JSON data. I just assign the data that is obtained from the server to the data.products variable on the controller scope. The error function assigns the object passed by AngularJS to describe the problem to the data.error variable on the scope. (I’ll return to the error in the next section.)
You can see the effect of making the Ajax request in Figure 7-1. When AngularJS creates its instance of the sportsStore controller, the HTTP request is started, and then the scope is updated with the data when it arrives. The product detail, category, and page features that I created in Chapter 6 operate just as they did before, but with the product data delivered from the Deployd server.
1. Handling Ajax Errors
Dealing with successful Ajax requests is easy because I just assign the data to the scope and let AngularJS update all of the bindings and directives in the views. I have to work a little harder to deal with errors and add some new elements to the view that I will display when there is a problem. In Listing 7-2, you can see the changes that I have made to the app.html file to display errors to the user.
Listing 7-2. Displaying Errors in the app.html File
<!DOCTYPE html>
<html ng-app=”sportsStore”>
<head>
<title>SportsStore</title>
<script src=”angular.js”></script>
<link href=”bootstrap.css” rel=”stylesheet” />
<link href=”bootstrap-theme.css” rel=”stylesheet” />
<script>
angular.module(“sportsStore”, [“customFilters”]);
</script>
<script src=”controllers/sportsStore.js”></script>
<script src=”filters/customFilters.js”></script>
<script src=”controllers/productListControllers.js”></script>
</head>
<body ng-controller=”sportsStoreCtrl”>
<div class=”navbar navbar-inverse”>
<a class=”navbar-brand” href=”#”>SPORTS STORE</a>
</div>
<div class=”alert alert-danger” ng-show=”data.error”>
Error ({{data.error.status}}). The product data was not loaded.
<a href=”/app.html” class=”alert-link”>Click here to try again</a>
</div>
<div class=”panel panel-default row” ng-controller=”productListCtrl”
ng-hide=”data.error”>
<div class=”col-xs-3″>
<a ng-click=”selectCategory()”
class=”btn btn-block btn-default btn-lg”>Home</a>
<a ng-repeat=”item in data.products | orderBy:’category’ | unique:’category'”
ng-click=”selectCategory(item)” class=” btn btn-block btn-default btn-lg”
ng-class=”getCategoryClass(item)”>
{{item}}
</a>
</div>
<div class=”col-xs-8″>
<div class=”well”
ng-repeat=
“item in data.products | filter:categoryFilterFn | range:selectedPage:pageSize”>
<h3>
<strong>{{item.name}}</strong>
<span class=”pull-right label label-primary”>
{{item.price | currency}}
</span>
</h3>
<span class=”lead”>{{item.description}}</span>
</div>
<div class=”pull-right btn-group”>
<a ng-repeat=
“page in data.products | filter:categoryFilterFn | pageCount:pageSize”
ng-click=”selectPage($index + 1)” class=”btn btn-default”
ng-class=”getPageClass($index + 1)”>
{{$index + 1}}
</a>
</div>
</div>
</div>
</body>
</html>
I have added a new div element to the view, which shows an error to the user. I have used the ng-show directive, which hides the element it applied to until the expression specified in the attribute value evaluates to true. I have specified the data.error property, which AngularJS takes as an instruction to show the div element when the property has been assigned a value. Since the data.error property is undefined until an Ajax error occurs, the visibility of the div element is tied to the outcome of the $http.get method in the controller.
The counterpart to the ng-show directive is ng-hide, which I have applied to the div element that contains the category buttons and the product details. The ng-hide directive will show an element and its contents until its expression evaluates to true, at which point they will be hidden. The overall effect is that when there is an Ajax error, the normal content is hidden and replaced with the error, as shown in Figure 7-2.
■ Tip I describe the ng-show and ng-hide directives in detail in Chapter 10. I created this screenshot by changing the value of the dataUrl in the sportsStore.js file to one that doesn’t exist, such as http://localhost:5500/doesNotExist.
The object passed to the error function defines status and message properties. The status property is set to the HTTP error code, and the message property returns a string that describes the problem. I included the status property in the message that I show to the user, along with a link that lets them reload the application and, implicitly, try to load the data again.
Source: Freeman Adam (2014), Pro AngularJS (Expert’s Voice in Web Development), Apress; 1st ed. edition.