Chapter 4. Navigating the single page

This chapter covers

  • An introduction to client-side routing
  • An overview of routes and their configuration
  • A summary of how routers work
  • How to use route parameters

Part 1 of this book covered views and how they’re created. What it didn’t cover, though, is client-side routing. This can be used to navigate from one view to the next, but that’s only part of the picture. Client-side routing is also about transitioning between different states in the application. This could include modifications to the current view without navigation or other activities that don’t include changes in the UI at all.

In this chapter, you’ll explore how users navigate in an SPA by using client-side routing. You’ll not only learn what client-side routers are but also peek behind the curtain a bit to see how they work.

During our discussion, I’ll keep things as framework-agnostic as possible by illustrating with pseudocode. Once you’ve nailed down the concepts, you’ll use what you’ve learned to create a simple routing example with AngularJS. In the example, you’ll create a website for a department of a university. Though rudimentary, the example will sufficiently illustrate the basic concepts common to most client-side routers without getting into too many vendor-specific details. You’ll also use that same university theme throughout our discussion. This will help you put a real-world face on the pseudocode and illustrations.

4.1. What is a client-side router?

Let’s begin this discussion by putting navigation as a whole into context. You’ll start with the request and response model used in traditional web application navigation.

4.1.1. Traditional navigation

In a traditional web application, navigation is thought of in terms of complete web pages. When you enter a new URL into the location bar of a browser, usually a request is sent for the page from the browser to a server, which responds by sending you back an entire HTML page (see figure 4.1).

Figure 4.1. In traditional web-page navigation, complete pages are sent back to the browser, triggering a refresh to display the new content.

What’s actually sent back is the HTML document for the requested page. After the HTML for that page is received, the browser then fetches any other source files referenced by the document, such as any CSS and JavaScript include files. Other assets referenced in the HTML, such as images, also get downloaded as the browser parses the document’s HTML and encounters their tags. To display the new content, the browser performs a complete refresh.

4.1.2. SPA navigation

As you already learned, an SPA’s DOM typically starts off as a shell in the SPA’s index page. That’s all that’s needed. The SPA’s modules and MV* framework, including supporting libraries, are either downloaded with the index page or asynchronously loaded if an AMD script-loading library is used. An SPA also has the ability to asynchronously fetch data and any remote templates (partials) and other resources not already included and to dynamically render views as needed.

As the user navigates, views are seamlessly displayed. This, combined with the asynchronous fetching of data, gives the application a smooth, native-like feel that makes for a great user experience. No more of the jarring interruptions that you usually experience when a page is wiped clean and a new page is downloaded and displayed. The SPA does all of this without refreshing the initial page after it’s loaded.

After the SPA is loaded, however, users need a way to access additional content within the application. Because an SPA is still a web-based application, your users will expect to be able to use the address bar and the browser’s navigation buttons for navigation. But how can navigation in an SPA occur with just one page and no browser refreshes?

As it turns out, it’s not only possible, it’s easy. The JavaScript component that makes navigation in this single-page environment possible is called a client-side router (or just a router).

But remember that when I talk about navigation, I’m talking about managing the state of the SPA’s views, data, and business transactions as the user navigates. The router manages the application’s state by assuming control of the browser’s navigation, allowing developers to directly map changes in the URL to client-side functionality (see figure 4.2). I’ll get into the details of how it does this later, in section 4.3.

Figure 4.2. In an SPA, the client-side router assumes control of navigation, allowing the SPA to display new views instead of complete pages.

Using this approach, no server round-trips are needed. The router determines when a change in state is needed through various methods of detecting changes in the browser’s location, such as listening for particular events. Anytime a URL change occurs, the router tries to match part of the new URL with an entry in its configuration.

Before breaking down the typical parts of a router’s configuration, let’s look at the big picture. Figure 4.3 provides an overview of the navigation process in an SPA and highlights the role of the client-side router. Notice that at no time does the router interact with the server. All routing is done in the browser.

Figure 4.3. An overview of the SPA navigation process and the role of the router

As you can see in the diagram, when the router matches paths from its configuration with real URLs in the browser, it can determine what types of changes in the application’s state should occur. Are there any changes in the data of the current view? Is a business process associated with the route? Should the route result in a change in the current view?

Now that you have a mental picture of the basic routing process, let’s go over how routes are set up. The next section breaks down the configuration of a router and defines its typical parts.

4.2. Routes and their configuration

No matter which router you use, a certain amount of up-front configuration must be done. You must make entries in the router’s configuration file to map out how the router should respond as the user navigates.

Each entry in the router’s configuration is called a route. Routes are stored in the router’s configuration at development time, with each route representing a logical flow within your SPA. You’ll typically design your routes as you design the layout for your application. Though router configuration varies from router to router, here are some typical configuration terms:

  • Name —With some routers, a name is given to the route. In others, the path serves as the route’s identifier.
  • Verb —Routes are sometimes defined with function names that match HTTP verbs—for example, get() or put()—but not always. Some routers use more-generic names such as on() or when() or route().
  • Path —The path is a part of the URL. The path used to configure the router establishes a link between a URL and a route/route handler. This allows the router to figure out which set of actions to perform. Anytime the URL in the browser changes, the router compares the new URL with all route paths in the configuration file’s path list for a match. When one is found, the route is carried out. The path of the route must be a valid part of a URL. Although it’s quite common that the path is simple text, some routers allow the use of regular expressions.
  • Functionality —This is the associated code that may be executed, such as a controller or a callback function. For the route to do anything, you need some associated code to run.

Routers may or may not include a way to define the view via configuration. Remember that routing is about changes in the application’s state that don’t have to result in a view at all. I include it in the list because the view is a configuration item for some routers:

  • A view —Most often when a router allows you to include the view as part of the route’s configuration, it’s the path to an HTML partial. As you may remember from chapter 2, these files are kept separate at development time and include only elements for a particular view. When the view is configured as part of the route, the router typically handles its display and gives the functionality access to the view (or a proxy object for the view, such as a ViewModel).

Figure 4.4 shows an average route in the configuration of some routers, like the one we’re using in this chapter. Because each router is different, this figure won’t be a perfect fit for all routers. You can use it, however, as a general, high-level look at route configuration. Keep in mind that in your SPA, you’ll most likely end up with many routes in the router’s configuration when your application is completed.

Figure 4.4. Router configuration entries serve as instructions for what happens when a route’s path matches a part of the browser’s URL.

Routers may also provide other, more advanced features. This varies from router to router. Consult the documentation of the router implementation you’re using for the full list of available configuration options.

4.2.1. Route syntax

Syntactically, the code for your routes will differ somewhat from router to router. With some router implementations, you configure the route and route handler in separate places, whereas others provide for the configuration of both in the same place. A common theme exists, though, for executing routes. Let’s look at some syntax examples.

Table 4.1 is by no means an exhaustive list, but it does give you a few examples. Remember to consult your router’s documentation for the exact syntax to be used.

Table 4.1. Examples of client-side router syntax

Framework/library

Path example

Sammy.js http://sammyjs.org get('#/routes/faculty', function() {...})
Kendo UI http://www.telerik.com/kendo-ui route("/routes/faculty", function() {...})
AngularJS https://angularjs.org when("/routes/faculty", { ... })
Backbone.js (2 steps) http://backbonejs.org 1. routes: {"/routes/faculty": "facultyRoute"} 2. on("route:facultyRoute", function () {...})

Also remember that some MV* frameworks, such as Knockout, don’t include a router. In that case, you’ll need to find an external router library, such as Sammy.js.

4.2.2. Route entries

Now let’s see how a complete route entry might look. Again, we’ll use pseudocode right now and wrap things up with a real, working project at the end of the chapter. The following listing illustrates a basic route in which you’re able to specify a view in the configuration.

Listing 4.1. Router with view capabilities (pseudocode)

This route entry almost reads like a sentence, doesn’t it? It’s telling the narrative of what needs to happen when the router finds a match to its path in the URL. It’s also an easy trail to follow: match the pattern, run the code, and show the result.

Next, let’s see the same route, but from the perspective of a router that leaves the view’s rendering and display up to you. In the following listing, the router provides only the facility to match the route with a controller or callback function.

Listing 4.2. Router that lets you handle the view (pseudocode)

This type of router is handy when you’re using a code-driven framework, such as Backbone.js.

Tip

No matter which type of router you use, try to leave your route configuration file devoted to the routes themselves. It’s considered a better practice to have the route’s callback function use your application’s modules to perform any business logic, as opposed to mixing in code not related to routing.

So far we’ve been talking about basic routes. Most routers offer a way to greatly expand the capabilities of a single route, by turning the route path into something dynamic. The next section explores the topic of route parameters and how to use them in your application.

4.2.3. Route parameters

Most routers have the notion of route parameters. A route parameter is a variable defined in the route’s path. This allows you to add variables to the URL, which will later execute when the route is carried out.

Why would you need this ability? Well, you might want to use the same functionality and the same view but want a different outcome for different situations. Route parameters provide this flexibility. This is a powerful tool to have at your disposal.

Let’s illustrate this concept with a route that displays the office hours for the faculty members in our university-themed example. In this situation, everything is the same in your route, down to the view. But you need the view to display different information depending on which faculty member’s name is clicked. To do this, you’ll pass the faculty member’s ID in the route’s path. This situation is a perfect scenario for using a route parameter.

To make the route parameter work, you need two parts: the relative URL containing the text you’re passing and the route path with the parameter on the receiving end.

Configuring a route path with a parameter

To tell the router that part of the path is a parameter, you define it in your route’s configuration. The parameter is defined by using special characters as specified by the router. The following listing shows your office hours route with its route parameter.

Listing 4.3. Office hours route with a parameter (pseudocode)

Each router has its own syntax for route parameters. Table 4.2 contains several examples of route parameter syntax for comparison. We’ll stick to the same router list that you saw previously in table 4.1. Each one happens to use the same route parameter syntax.

Table 4.2. Examples of route parameter syntax

Framework/library

Path example

Sammy.js http://sammyjs.org :facultyNameParam
Kendo UI http://www.telerik.com/kendo-ui :facultyNameParam
AngularJS https://angularjs.org :facultyNameParam
Backbone.js (2 steps) http://backbonejs.org :facultyNameParam

In the frameworks/libraries represented in table 4.2, a colon is the common way to denote a route parameter. But no universal standard exists. Other routers may use a different convention. Additionally, the router you select may offer more-advanced parameter options, such as regular expressions. Consult the documentation for whichever router you choose.

The relative URL with the text to be passed

If you use a route parameter, the router can discern from the path which part of the URL is the parameter and which part should be a verbatim match. For example, the following is a link that matches the route from listing 4.3. The URL matches the path exactly, except for the parameter portion.

<a href="#/routes/officehrs/manderson">
    Dr. Mary Anderson
</a>

If this were a link to your office hours route, the last part of the link’s URL would represent this person’s faculty ID. Each faculty member on your fictitious university web page could have a link that uses the same route pattern but includes a different ID as the URL’s suffix.

Multiple parameters

Most (if not all) routers allow for more than one parameter to be used at a time. For example, if you have more than one link for a faculty member, you might want to parameterize the route even further:

/routes/officehrs/{facultyNameParam}/{dayOfTheWeek}

Apart from defining specific routes, it’s also possible to include a default route.

4.2.4. Default routes

To round out our discussion of routes and their configuration, we need to touch on default routes. These types of routes are good for catchall situations when the route isn’t specified or is invalid. Without a default, your application wouldn’t do anything if the user typed a URL in the address bar that had no matches in the router’s configuration. With a default route in place, you’re immediately redirected to a particular route from your route list. The following listing shows how you can redirect any URL back to the faculty route when no match is found.

Listing 4.4. Default route (pseudocode)

Your default means that when you have a route match, route to it; otherwise, redirect to the faculty route. The default route is also a handy mechanism to specify what happens when a user types a website’s base URL into the browser, with no specific paths included.

The next section explains what the router is doing when you navigate. The discussion will stay at a high level but go just deep enough for you to understand what’s going on behind the scenes during routing.

4.3. How do client-side routers work?

Part of a client-side router’s job is to allow users to use the address bar and the browser’s navigation buttons as they normally would in a traditional web application. At a minimum, many client-side routers offer the following features that make this possible:

  • Match patterns in the URL with paths defined by the route
  • Allow for the execution of code in your application when a match is found
  • Allow a view to be specified that will be displayed when the route is triggered
  • Allow for parameters to be passed via the route’s path
  • Allow users to use standard navigation methods of the browser to navigate the SPA

These features are all that are needed to provide a minimal level of navigation in an SPA. Keep in mind, though, that there’s no guaranteed standard that all client-side routers must follow. These are just the most common options you’ll encounter. The documentation for the MV* framework (or independent router library) will list its full range of features.

Having summarized a basic list of features most routers offer, let’s peek under the covers to see how routers provide navigation in a single-page setting.

Routers use one of two methods: either via the URL’s fragment identifier or the HTML5 History API. Both methods enable the router to provide server-less navigation but in slightly different ways. Because the HTML5 History API is newer and not supported by older browsers, you’ll start your foray into the world of client-side routing with the more traditional fragment identifier method.

4.3.1. The fragment identifier method

The traditional method for routers to provide navigation in an SPA is via a fragment identifier. As noted in figure 4.5, the fragment identifier is any arbitrary string of text at the end of the URL and is prefixed with a hash symbol (#). This optional part of the URL references a section of the current document, not a new document.

Figure 4.5. The fragment identifier

Browsers treat this part differently from the rest of the URL. When a new fragment identifier is added to the URL, the browser doesn’t attempt to interact with the server. The addition does, however, become a new entry in the browser’s history.

This is important because all entries in the browser’s history, even those generated from the fragment identifier, can be navigated to via normal means, such as the address bar and the navigation buttons. To see this in action, go to any website, such as www.manning.com. Next, try executing the following in your browser’s console:

window.location.hash = "hello";

As you’ll see, doing this results in hello being added as the URL’s fragment identifier. The URL should look like this after the line executes:

http://www.manning.com/#hello

This action also adds a new entry in the browser’s history. Now you can navigate back and forth between the fragment identifier and the original URL.

Exploiting the browser’s location object

The location object contains an API that allows you to access the browser’s URL information. In an SPA, routers take advantage of the location object to programmatically gain access to the current URL, including the fragment identifier. The router does this to listen for changes in the fragment identifier portion of the URL, via the window’s onhashchange event (if available in that browser version—otherwise, it polls for hash changes).

When a change occurs, the pattern in the new hash string is compared to all the paths in each route from the router’s configuration. If a match exists, the router executes any process specified and then displays the view from the matching route.

For example, imagine you have a link to your fictitious department’s main contact page in your website header. The link’s code points to a fragment identifier URL:

<a href="#routes/contact">Contact Us</a>

When you click this link, the browser’s fragment identifier changes from its initial value to #/routes/contact.

Because the router actively listens for changes in the fragment identifier, this new hash is detected. Upon detection, the router searches all routes in its configuration for a path matching /routes/contact. When it finds a match, the route in the following listing is carried out.

Listing 4.5. Main contact route (pseudocode)

You should now have a pretty good understanding of basic client-side routing. As I mentioned at the beginning of this section, routers can use two methods to control the application’s state. You’ve looked at the fragment identifier method. In the next section, you’ll look at the newer HTML5 History API method.

4.3.2. The HTML5 History API method

You’ve learned that by using the fragment identifier method to change the URL’s hash information, the router can add new navigable entries in the browser’s history. Each change adds a new entry in the history stack. After that, users can navigate back and forth between hashes without triggering a page refresh. But when using this method, developers are forced to create paths that revolve around the hash symbol (#).

New methods in the HTML5 History API change this. Routers can take advantage of new functionality available in HTML5 to interact with the browser’s history without relying on the fragment identifier. Also, because these methods aren’t available in older browsers, most routers gracefully fall back on the fragment identifier automatically.

pushState and replaceState

The two new methods in the History object’s API that routers can take advantage of are as follows:

  • pushState() —Allows you to add new history entries
  • replaceState() —Allows you to replace existing history entries with new ones

These new additions allow direct access to the browser’s history without relying on the fragment identifier. You’ll explore them briefly to understand what happens when routers use the HTML5 History API method.

Using history.pushState() or history.replaceState(), the router can directly modify the browser’s history stack. Both methods also allow the router to work with “pretty,” natural-looking URL segments instead of hashes. Both methods take three parameters:

  • State object —An optional JavaScript object associated with the history entry
  • Title —Represents a new title for the history entry (though not implemented by most browsers as of this writing)
  • URL —The URL that should be displayed in the browser’s address bar

To see how this method works, give pushState() a try. Go to any website, such as www.manning.com, and type following in your browser’s console:

history.pushState({myObject: "hi"},"A Title", "newURL.html");

The command results in the URL changing to this:

http://www.manning.com/newURL.html

It also adds a new entry in the browser’s history. Now you can navigate back and forth between the new URL and the original URL. You’ll also notice that the URL added via pushState() doesn’t trigger a browser refresh and doesn’t contain the hash symbol.

To view the state object that was added, you can type history.state into the console. In response, you’ll see the contents of myObject returned.

The popstate event

Finally, routers are given a way to monitor the history stack for changes: the window.popstate event. Browsers fire this event whenever the user navigates between history entries.

You can also experiment with this in your console. Use the pushState() method to add some history entries. Then execute the following code in your console:

window.addEventListener("popstate", function(event) {
    console.log("popstate event fired");
});

Next, navigate back and forth between the URLs added with pushState(). You should see the following log entry added to the console:

popstate event fired

Now that you understand the newer HTML5 History API method of routing, let’s see how you change your code to use it.

4.3.3. Changes for the HTML5 History API method

Most routers offer the option to use the HTML5 History API method for client-side routing. Indicating to the router which method you prefer is usually as easy as setting a single configuration option. Often, however, other changes need to be made in addition to the mode switch. I talk about those in this section.

Let’s start with the option to change methods. In many routers, you change a Boolean value from false to true.

HTML5 mode

To convert our example from the fragment identifier method to this one, you need to change the appropriate setting in your router’s configuration. This is where you flip the switch to use the HTML5 History API method. For example, in AngularJS, you’d use this:

html5Mode(true);

In Backbone.js, you’d use this:

Backbone.history.start({pushState: true});

Again, these are framework-specific examples. Consult your router’s documentation for the exact syntax.

BASE HREF

Now that you’ve told the router that you want to use the HTML5 History API method, you need to set the BASE HREF in your index page’s header:

<head>
<base href="/SPA/">
</head>

For the HTML5 History API to work correctly, your BASE HREF must match the deployed application’s root path in its base URL. Otherwise, you’ll get an “Error 404 not found” response when your application tries to retrieve the views in its routes.

Tip

You need a base URL only if you don’t want to include the full path in your links/code.

In this example, /SPA/ will be the root path in your base URL. So you need to use that as the BASE HREF. A lot of different servers are out there, and applications get deployed in many ways. As long the BASE HREF is set properly, your views will be displayed.

Server-side changes

Finally, to finish off the HTML5 History API configuration, you’ll need to configure your server so that it always returns content for the root. For example, if you have a catchall server-side route configured, it’ll always return the correct resource to the client.

One caveat is that if a user uses a bookmark or page refresh, the browser will make a request for that same content. One possible solution is to set up a redirect on the server to internally redirect to that same URL.

Removing the hash

If the router supports it, you can now remove the hash characters from the links in your views. For example, in the link to the main office contact information, the anchor tag can be written as follows:

<a href="routes/contact">Contact Us</a>

When you click this link, you’ll see what looks like a normal URL in the browser’s address bar. No hash character!

Now that you know the basics of client-side routing, you can roll up your sleeves and do some coding.

4.4. Putting it all together: implementing routing in SPAs

In this section, you’ll take the concepts discussed and illustrated with pseudocode and create a real, working project using AngularJS. In this example, you’ll continue the university theme. Let’s pretend you’re a campus IT staff member tasked with creating a website for one of the university’s departments.

You’ll have three views: a landing view with faculty names to choose from, another view to display the office hours for the person the user selects, and a general contact view for the department.

The user can navigate by either clicking the name of a faculty member in the landing view’s faculty list or clicking a navigation link in the header. Also, because you’re incorporating an SPA navigation component, the user will be able to use the browser’s address bar and navigation buttons.

I include the most relevant excerpts from the code here. The complete code is available for download when you want to give the example a try or see the complete code for this chapter’s example. Figure 4.6 shows the finished product.

Figure 4.6. For this example, you’ll create a basic website for a university department.

You’ll begin with the example’s first route, the faculty list route. This is your default route. As you may remember from earlier, the default route is the one used when there’s no match between the current URL and any of the paths in your configuration file. Because the site’s base URL won’t have a match, you should always execute the faculty list route by default. A direct link to the faculty route exists in the header, so this route can be accessed from there as well.

4.4.1. The faculty list (default route)

You saw the pseudocode for the faculty list in our discussion of routes, so let’s create it for real now.

When you use AngularJS’s router, your routes will be configured via $route-Provider. You can use the $routeProvider’s when() method for normal routes and otherwise() to configure a default route. This is specific to AngularJS, though, so don’t worry about that detail. If you’re using a different framework or routing library, you’ll add the same configuration using different syntax.

For now, let’s focus on the concept presented by the route itself. In this route, you’ll use the text /routes/faculty to represent the route’s path (see the following listing).

Listing 4.6. Faculty list route

As mentioned earlier, you also want to display the list of faculty members by default. The next listing shows your default route.

Listing 4.7. Default route

With this default route in place, you’re immediately redirected to your faculty list when you type the application’s URL into the browser.

The functionality behind the route

With AngularJS, you can give the name of a registered controller as the functionality to be executed. In other libraries or frameworks, it may be some other type of object or the name of a callback function. In this example, whenever the router finds a URL that contains /routes/faculty, the code in the facultyController controller is executed.

Your faculty list’s controller will provide the list of faculty members to be displayed in the view (see listing 4.8). This controller is one of the three defined in our example’s controller file, which also contains the definition for the routeControllers object:

var routerControllers = angular.module(
"RouterApp.controllers", []);

To be available in the view, your list needs to be added to AngularJS’s $scope object. As you may remember from chapter 2, $scope is a built-in object, which serves as the “middleman” between the view and your data (kind of like a ViewModel).

Listing 4.8. The functionality for your route

In the array you’re making available to the view, you have a list of faculty objects. Each object has two properties: hrefVal and displayText. The hrefVal property will be used in the creation of the link’s URL. The displayText property contains the name the faculty member. You’ll use the faculty member’s name as the visible text of the link.

Note

The most common practice when using AngularJS is to keep any kind of business logic in an AngularJS service, but we’re breaking that rule here for the sake of keeping this example as simple as possible.

The faculty list view

Finally, in listing 4.9, you see the source code for your view. As part of the state changes associated with this route, you’ll display this view, which will contain the data from listing 4.8. Notice that the view’s template uses the properties from the $scope object with the ng-repeat binding to iteratively create the list of links.

Listing 4.9. The route’s view

After the code fires, your view in facList.html is displayed. The view’s template is rendered for each entry in the array. The following listing shows what the first object in the array would look like if you were to inspect the DOM after the template has been rendered. Each HREF in each anchor tag now has a URL and display text.

Listing 4.10. DOM view of rendered template

Tip

In AngularJS, you use the special directive ng-view to mark the area where views are to be rendered. You put this directive anywhere in your SPA’s shell, and the $route service will automatically find it and place its views there. Because this is AngularJS-specific syntax, this detail will vary depending on the MV* framework you’ve chosen.

4.4.2. The main contact route

Also in the navigation header of your site is a link to the fictitious department’s main contact view. The link’s code points to a fragment identifier URL:

<a href="#routes/contact">Contact Us</a>

When you click this link, the browser’s fragment identifier changes from #/routes/faculty to #/routes/contact (see figure 4.7).

Figure 4.7. Clicking the Contact Us link produces a new fragment identifier in the browser’s URL.

Because the router is actively listening for changes in the fragment identifier, this new hash is detected. Upon detection, the router searches all routes in its configuration for a path matching /routes/contact. When it finds a match, the route in the following listing is carried out.

Listing 4.11. Main contact route

The controller for this route is contrived, but it provides another view to demonstrate navigation (see the next listing). Again, any real business logic would be placed in another component, such as an AngularJS service.

Listing 4.12. The functionality for your route

In contactController, you have only one property: the main office’s contact number. The following listing shows the view for this route. It displays the main office’s telephone number via an expression binding.

Listing 4.13. The route’s view

4.4.3. Faculty office hours (parameterized route)

Let’s create one route with a parameter to show how they’re used. When this route is handled, you’ll display the office hours of the faculty member selected from your faculty list view. As with our pseudocode, you’ll start by creating a route with a placeholder for the selected faculty member’s ID (see the following listing).

Listing 4.14. Office hours route with a parameter

Each router will have different syntax for route parameters. Consult the documentation for whichever router you choose. This example uses facultyID as the variable name.

The relative URL with the text to be passed

If you view the source in your browser for the links that you print out in your view, each link points to the same route except for the last segment of the URL. Here’s the link for the first faculty member:

<a href="#/routes/officehrs/manderson" class="ng-binding">
     Dr. Mary Anderson
</a>

The last part of the link’s URL represents this person’s faculty ID. It was written dynamically with your binding. Because you have three faculty members in your list, each member’s URL will contain a different ID.

The controller

To be able to use the information passed via the route parameter, each framework or library will provide a way to access it in your code. AngularJS has the aptly named $routeParams object. The next listing illustrates your controller’s use of this variable.

Listing 4.15. Controller for office hours

In this example, the route parameter passed in will match one of the three contact information entries. Passing the route parameter (the faculty ID) as the property name of the contactInfo object returns the correct faculty member’s information. Then you can put the information returned into the $scope variable so the view can display it. The following listing provides the view for this route.

Listing 4.16. Office hours view

Now let’s look at the final product. When the user clicks the first link from your faculty list view, and the faculty ID of manderson is passed into the controller via the route parameter, the user sees the screen shown in figure 4.8.

Figure 4.8. Passing manderson via a route parameter results in the correct office hours being displayed.

Now that you understand routing, you’ll be using a router from this point forward. Feel free to refer to this chapter anytime you need a refresher on what you’ve learned.

4.5. Chapter challenge

Now here’s a challenge for you to see what you’ve learned in this chapter: create a simple picture viewer. You’ll take several images you have available and use client-side routing to display them. You could use a different route and resulting view for each picture. But if you want to make the challenge more interesting, try using a single route and use the name of the image as a route parameter. Then use the MV* framework to dynamically swap the pictures, using the image’s name passed in via the route parameter.

4.6. Summary

There was quite a lot of information to digest in this chapter. Let’s review to see what you’ve learned:

  • Routers are libraries/frameworks that allow you to specify a desired state for your application for a given URL.
  • Routes are configured in a router’s configuration.
  • Routing in an SPA occurs in the browser. No server requests are required.
  • Routers use one of two methods to achieve client-side routing: the fragment identifier or the HTML5 History API.
  • The HTML5 History API method for routing normally requires you to explicitly state in the router configuration that you want to use it. It also requires a few code changes, including changes on the server.
  • The same route can be used to display different outcomes by incorporating route parameters in a route’s path. Route parameters are variables defined in the path of the route that allow information to be passed via a URL.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset