SportsStore – A Application of AngularJS: Displaying the (Fake) Product Data

I am going to start by adding support for displaying the product data. I want to focus on one area of functionality at a time, so I am going to define fake local data initially, which I will then replace with data obtained from the Deployd server in Chapter 7.

1. Creating the Controller

I need to start with a controller, which, as I explained in Chapter 3, defines the logic and data required to support a view on its scope. The controller I am going to create will be used throughout the application—something I refer to as the top-level controller, although this is a term of my own invention—and I define this controller in its own file. Later, I’ll start to group multiple related controllers in a file, but I put the top-level controller in its own file. Listing 6-2 shows the contents of the controllers/sportsStore.js file, which I created for this purpose.

Tip The reason I keep the top-level controller in a separate file is so that I can keep an eye on it when it changes in a revision control system. The top-level controller tends to change a lot during the early stages of development, when the application is taking shape, and I don’t want the avalanche of change notifications to mask when other controllers are being altered. Later in the project, when the main functionality is complete, the top-level controller changes infrequently, but when it does change, there is a potential for breaking pretty much everything else in the application. At that point in the development cycle, I want to know when someone alters the top-level controller so that I can ensure that the changes have been thought through and fully tested.

Listing 6-2. The Contents of the sportsStore.js File

angular.module(“sportsStore”)

.controller(“sportsStoreCtrl”, function ($scope) {

$scope.data = {

products: [

{ name: “Product #1”, description:

category: “Category #1”, price

{ name: “Product #2”, description:

category: “Category #1”, price

{ name: “Product #3”, description:

category: “Category #2”, price

{ name: “Product #4”, description:

category: “Category #3”, price: 202 }]

}

});

Notice that the first statement in this file is a call to the angular.module method. This is the same method call that I made in the app.html file to define the main module for the SportsStore application. The difference is that when I defined the module, I provided an additional argument, like this:

angular.module(“sportsStore”, []);

The second argument is an array, which is currently empty, that lists the modules on which the sportsStore module depends and tells AngularJS to locate and provide the functionality that these modules contain. I’ll be adding elements to this array later, but for now it is important to know that when you supply the array—empty or otherwise—you are telling AngularJS to create a new module. AngularJS will report an error if you try to create a module that already exists, so you need to make sure your module names are unique.

By contrast, the call to the angular.module method in the sportsStore.js file doesn’t have the second argument:

angular.module(“sportsStore”)

Omitting the second argument tells AngularJS that you want to locate a module that has already been defined.

In this situation, AngularJS will report an error if the module specified doesn’t exist, so you need to make sure the module has already been created.

Both uses of the angular.module method return a Module object that can be used to define application functionality. I have used the controller method that, as its name suggests, defines a controller, but I describe the full set of methods available—and the components they create—in Chapters 9 and 18. You will also see me use some of these methods as I build the SportsStore application.

Note I wouldn’t usually put the call to create the main application module in the HTML file like this because it is simpler to put everything in the JavaScript file. The reason I split up the statements is because the dual uses of the angular.module method cause endless confusion and I wanted to draw your attention to it, even if that means putting a JavaScript statement in the HTML file that could be omitted.

The main role of the top-level controller in the SportsStore application is to define the data that will be used in the different views that the application will display. As you will see—and as I describe in detail in Chapter 13—an AngularJS can have multiple controllers arranged in a hierarchy. Controllers arranged in this way can inherit data and logic from controllers above them, and by defining the data in the top-level controller, I can make it easily available to the controllers that I will be defining later.

The data I have defined is an array of objects that have the same properties as the data that is stored by Deployd, which allows me to get started before I start making Ajax requests to get the real product information.

2. Displaying the Product Details

To display details of the products, I need to add some HTML markup to the app.html file. AngularJS makes it easy to display data, as Listing 6-3 shows.

Listing 6-3. Displaying Product Details 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”, []);

</script>

<script src=”controllers/sportsStore.js”></script>

</head>

<body ng-controller=”sportsStoreCtrl”>

<div class=”navbar navbar-inverse”>

<a class=”navbar-brand” href=”#”>SPORTS STORE</a>

</div>

<div class=”panel panel-default row”>

<div class=”col-xs-3″>

Categories go here

</div>

<div class=”col-xs-8″>

<div class=”well” ng-repeat=”item in data.products”>

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

</div>

</body>

</html>

There are three different kinds of changes highlighted in this listing. The first is that I have added a script element that imports the sportsStore.js file from the controllers folder. This is the file that contains the sportsStoreCtrl controller. Because I defined the sportsStore module in the app.html file and then located and used it in the sportsStore.js file, I need to make sure the inline script element (the one that defines the module) appears before the one that imports the file (which extends the module).

The next change is to apply the controller to its view using the ng-controller directive, like this:

<body ng-controller=”sportsStoreCtrl”>

I will be using the sportsStoreCtrl controller to support the entire application, so I have applied it to the body element so that the view it supports is the entire set of content elements. This will start to make more sense when I begin to add other controllers to support specific features.

3. Generating the Content Elements

The last set of changes in Listing 6-3 creates the elements to display details of the products for sale in the SportsStore. One of the most useful directives that AngularJS provides is ng-repeat, which generates elements for each object in an array of data. The ng-repeat directive is applied as an attribute whose value creates a local variable that is used for each data object in a specified array, like this:

<div class=”well” ng-repeat=”item in data.products“>

The value I have used tells the ng-repeat directive to enumerate the objects in the data.products array applied to the scope by the controller for the view and assign each object to a variable called item. I can then refer to the current object in data binding expressions, which are denoted with the {{ and }} characters, like this:

<div class=”well” ng-repeat=”item in data.products”>

<h3>

<strong>{{item.name}}</strong>

<span class=”pull-right label label-primary”>{{item.price | currency}}</span>

</h3>

<span class=”lead”>{{item.description}}</span>

</div>

The ng-repeat directive duplicates the element to which it is applied (and any descendant elements) for each data object. That data object is assigned to the variable item, which allows me to insert the values of the name, price, and description properties as required.

The name and description values are inserted as-is in the HTML elements, but I have done something different with the price property: I have applied a filter. A filter formats or orders data values for display in a view. AngularJS comes with some built-in filters, including the currency filter, which formats numeric values as currency amounts. Filters are applied by using the | character, followed by the name of the filter, such that the expression item.price | currency tells AngularJS to pass the value of the price property of the item object through the currency filter.

The currency filter formats amounts as U.S. dollars by default, but, as I explain in Chapter 14, you can use some AngularJS localization filters to display other currency formats. I describe the built-in filters and show you how to create your own in Chapter 14. I will also create a custom filter in the next section. The result is that a set of elements like this one is generated for each element:

<div class=”well ng-scope” ng-repeat=”item in data.products”>

<h3>

<strong class=”ng-binding”>Product #1</strong>

<span class=”pull-right label label-primary ng-binding”>$100.00</span>

</h3>

<span class=”lead ng-binding”>A product</span>

</div>

Notice now AngularJS has annotated the elements with classes that begin with ng-. These are an artifact of AngularJS processing the elements and resolving data bindings, and you should not attempt to change them. You can see the visual effect changes in Listing 6-3 by loading the app.html file in the browser, as shown in Figure 6-7. I have shown only the first couple of products, but all of the details are displayed in a single list (something I will address by adding pagination later in this chapter).

Source: Freeman Adam (2014), Pro AngularJS (Expert’s Voice in Web Development), Apress; 1st ed. edition.

Leave a Reply

Your email address will not be published. Required fields are marked *