SportsStore – Navigation and Checkout with AngularJS: Adding URL Navigation

Before I go any further and add support for checking out, I am going to enhance the infrastructure of the SportsStore application by adding support for URL routing. I describe URL routing in detail in Chapter 22, but the short version is that it allows for different partial views to be displayed automatically based on the current URL. This makes it easier to build larger applications that the user can navigate freely around, and I will use it as the foundation for displaying the views that the user needs to complete their purchase and submit an order to the server.

To get started, I need to create a view that I will display when the user begins the checkout process. Listing 7-11 shows the contents of the views/checkoutSummary.html file, which contains some placeholder content for the moment. I’ll return to this file and add the real content once I have set up the URL routing feature.

Listing 7-11. The Contents of the checkoutSummary.html File

<div class=”lead”>

This is the checkout summary view

</div>

<a href=”#/products” class=”btn btn-primary”>Back</a>

1. Defining URL Routes

I am going to start by defining the routes I require, which are the mappings between specific URLs and the views that should be displayed when the browser navigates to that URL. The first two will map the /product and /checkout URLs to the productList.html and checkoutSummary.html views, respectively. The other will be a catchall route that will display the productList.html view by default. Listing 7-12 shows the changes I have made to implement routing in the app.html file.

Listing 7-12. Adding Support for URL Routing 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”, “cart”, “ngRoute”])

.config(function ($routeProvider) {

$routeProvider.when(“/checkout”, {

templateUrl: “/views/checkoutSummary.html”

});

$routeProvider.when(“/products”, {

templateUrl: “/views/productList.html”

});

$routeProvider.otherwise({

templateUrl: “/views/productList.html”

});

});

</script>

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

<script src=”filters/customFilters.js”></script>

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

<script src=”components/cart/cart.js”></script>

<script src=”ngmodules/angular-route.js”></script>

</head>

<body ng-controller=”sportsStoreCtrl”>

<div class=”navbar navbar-inverse”>

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

<cart-summary />

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

<ng-view />

</body>

</html>

I have added a script element to import the angular-route.js file into the application. The functionality that this file provides is defined in a module called ngRoute, which I have declared as a dependency of the sportsStore module.

To set up my routes, I have called the config method on the module object. The config method takes a function as its argument, which is executed when the module is loaded but before the application is executed, providing an opportunity for any one-off configuration tasks.

The function that I passed to the config method declares a dependency on a provider. As I mentioned earlier, there are different ways to create AngularJS services, and one of them creates a service that can be configured through a provider object, whose name is the concatenation of the service name and Provider. The $routeProvider that I have declared a dependency on is the provider for the $route service and is used to set up the URL routing in an application.

Tip I explain how to create services with providers in Chapter 18 and how to use the $route service and the $routeProvider in Chapter 22.

I use two methods defined by the $routeProvider object to set up the routes I require. The when method allows me to match a URL to a view, like this:

$routeProvider.when(“/checkout”, {

templateUrl: “/views/checkoutSummary.html”

});

This statement tells AngularJS that when the URL is /checkout, I want the /views/checkoutSummary.html file to be displayed. The otherwise method specifies the view that should be used when the URL doesn’t match one of those defined by the when method. It is always sensible to define such a fallback route, and mine specifies the /views/ ProductList.html view file.

URL routes are matched against the path section of the current URL and not the complete URL. Here is a URL that would match the route shown earlier:

http://localhost:5000/app.html#/checkout

I have highlighted the path, which follows the # character in the URL. AngularJS doesn’t monitor the whole URL because a URL such as http://localhost:5000/checkout would cause the browser to dump the AngularJS application and try to load a different document from the server—something that is rarely required. This point causes a lot of confusion, so I have summarized the effect of my URL routing policy in Table 7-3.

Tip As I describe in Chapter 22, you can enable support for using the HTML5 History API, which changes the way URLs are monitored so that something like http://localhost:5000/checkout will work. Caution is required because browser implementations differ, and it is easy to confuse the user because the browser will attempt to load a different document if they try to edit the URL manually.

1.1. Displaying the Routed View

The routing policy defines which views should be displayed for given URL paths, but it doesn’t tell AngularJS where to display them. For that I need the ng-view directive, which is defined in the ngRoute module along with the other routing features. In Listing 7-12, I replaced the ng-include directive with ng-view, as follows:

<body ng-controller=”sportsStoreCtrl”>

<div class=”navbar navbar-inverse”>

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

<cart-summary />

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

<ng-view />

</body>

There are no configuration options or settings required; just adding the directive tells AngularJS where it should insert the content of the currently selected view.

2. Using URL Routing to Navigate

Having defined my URL routes and applied the ng-view directive, I can change the URL path to navigate through the application. My first change is to the Checkout button displayed by the cart summary widget that I created earlier in the chapter. Listing 7-13 shows the change I made to the cartSummary.html file.

Listing 7-13. Using URL Path Navigation to the cartSummary.html File

<style>

.navbar-right { float: right limportant; margin-right: 5px;}

.navbar-text { margin-right: 10px; }

</style>

<div class=”navbar-right”>

<div class=”navbar-text”>

<b>Your cart:</b>

{{itemCount()}} item(s),

{{total() | currency}}

</div>

<a href=”#/checkout” class=”btn btn-default navbar-btn”>Checkout</a>

</div>

I updated the a element to add an href attribute whose value changes the path. Clicking the element will cause the browser to navigate to the new URL (which is local to the already-loaded document). The navigation change is detected by the AngularJS routing service, which causes the ng-view directive to display the checkoutSummary.html view, as illustrated by Figure 7-6.

Notice that the URL displayed by the browser changes from the initial starting point of http://localhost:5000/ app.html to http://localhost:5000/app.html#/checkout. You can click the Back button displayed by the checkoutSummary.html view, which I configured in Listing 7-12 to move to the /products path, as follows:

<a href=”#/products” class=”btn btn-primary”>Back</a>

The main benefit of using URL routing is that components can change the layout shown by the ng-view directive without having any prior knowledge of the view that will be shown, the location or disposition of the ng-view directive, or sharing components (such as controllers or services) with the view that will be displayed. This makes it easier to scale up complex applications and makes it possible to change the behavior of the application just by changing the URL routing configuration.

Tip You can also return to the products listing by manually editing the URL to be http://localhost:5000/app. html#/products or http://localhost:5000/app.html#. Note the trailing # character in that last URL. If you omit it, the browser will interpret the URL as a request to load the app.html page, which will cause any unsaved state to be lost. For the SportsStore application, that means the contents of the cart will be lost. The finicky nature of the URLs means that the user can edit them directly but that the results can be unexpected with even the slightest error.

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 *