6.8. Adding History Support

Adding history support is just a matter of calling the equivalent client-side APIs on the Sys.Application client class instead of the ScriptManager. Listing 6-7 (AlbumSearchClientHistory.aspx) shows the necessary changes.

Example 6-7. AlbumSearchClientHistory.aspx
<%@ Page Language="C#" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/
    TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Phish Album Search</title>
    <script type="text/javascript">
    var Albums = [
        { title: "A Live One", date: "1995/06/27" },
        { title: "Hampton Comes Alive", date: "1999/11/23" },
        { title: "Live In Vegas", date: "2002/11/12" },
        { title: "Vida Blue", date: "2002/06/25" },
        { title: "The Illustrated Band (Vida Blue)", date: "2003/10/14" }
    ];

    function onSearch() {
        var term = $get('txtSearch').value.toLowerCase();
        var title = 'Phish Album Search - "' + term + '"';
        Sys.Application.addHistoryPoint({ term: term }, title);
    }

    function executeSearch() {
        var term = $get('txtSearch').value.toLowerCase();
        var results = [];
        for (var i = 0; i < Albums.length; i++) {
            var album = Albums[i];
            if (album.title.toLowerCase().indexOf(term) !== −1) {
                results.push(album);
            }
        }
        printResults(results);
        var title = 'Phish Album Search - "' + term + '"';
        document.title = title;
    }

    function printResults(results) {
        var list = $get('albumList'),
        list.innerHTML = "";

        var sb = new Sys.StringBuilder();

for (var i = 0; i < results.length; i++) {
            var album = results[i];
            if (!sb.isEmpty()) {
                sb.append("<hr/>");
            }
            sb.append("<div style='font-weight:bold'>");
            sb.append(album.title);
            sb.append("</div><div style='font-style:italic'>Released ");
            sb.append(new Date(album.date).format("MMM dd, yyyy"));
            sb.append("</div>");
        }
        list.innerHTML = sb.toString();
    }

    function onNavigate(sender, args) {
        $get('txtSearch').value = (args.get_state().term || "");
        executeSearch();
    }
    </script>
</head>
<body>
    <form id="form1" runat="server">
    <asp:ScriptManager ID="ScriptManager1" runat="server"
        EnableHistory="true" ClientNavigateHandler="onNavigate" />

    <input type="text" id="txtSearch" />
    <input type="button" value="Search" onclick="onSearch()" />

    <div id="albumList">
    </div>
    </form>
</body>
</html>

First, you set the EnableHistory property of the ScriptManager to true, just like for server-side support. Even though you are using history points only client-side, you must still enable it. Actually, it is possible to use client-side history without enabling it on the ScriptManager, and in fact, without a ScriptManager or even ASP.NET, but doing so requires setting up an IFRAME in Internet Explorer. Using the document fragment for history management in a manner that works across all browsers is tricky business, and this just happens to be one of the requirements for it to work in Internet Explorer. ScriptManager does this for you automatically. If you are still interested in doing it manually, you can attempt to use history to see a very detailed error message explaining the requirements, as in Figure 6-7.

Next, you set the ScriptManager ClientNavigateHandler property. This is a shortcut to declaring an event handler for the client-side Sys.Application.navigate event, equivalent to the server-side ScriptManager.Navigate event. You may also hook into in the usual client-side way, but be sure to do it with inline code inside of the <form> element. Putting it in the <head> element fails because the Sys.Application class has not been created yet at that point, and putting it inside the pageLoad function is too late, as a navigate event occurs before the Sys.Application.load event occurs if the user refreshes the page or links to your page with the document fragment already set:

Sys.Application.add_navigate(onNavigate);

Figure 6-7. Figure 6-7

To add a history point, call the Sys.Application.addHistoryPoint method:

Sys.Application.addHistoryPoint({ term: term }, title);

The first argument accepts a JavaScript object that serves as a dictionary of key-value pairs. The { } block is a short-hand way of creating such an object in JavaScript. The second argument is the optional title of the history point.

Unlike the server-side, when adding a history point client-side, the navigate event is immediately fired. So rather than the search button executing the search and then adding the history point, it adds only the history point. That causes the ClientNavigateHandler to execute, which executes the search. If the search button directly executes the search, the search is performed twice: once when the search is executed and again when the navigate event fires as a result of adding the history point.

function onSearch() {
    var term = $get('txtSearch').value.toLowerCase();
    var title = 'Phish Album Search - "' + term + '"';
    Sys.Application.addHistoryPoint({ term: term }, title);
}

And again, just like the server-side navigate event handler, the client-side navigate event handler is responsible for restoring the state of the page at the time the history point was added.

function onNavigate(sender, args) {
    $get('txtSearch').value = (args.get_state().term || "");
    executeSearch();
}

The args parameter is of type Sys.HistoryEventArgs, which has a state property. The state is the name-value pair object containing all the history point data, so the term field is used to retrieve the value. However, it is possible for there to be no term, such as when the user clicks back all the way to when they first loaded the page and hadn't performed a search yet. In JavaScript, the term would be undefined. In that case, an empty string should be the search term. Using the or-operator || with an empty string ensures the value is an empty string in that case. Figure 6-8 shows the result after searching for "blue" and "live."

Figure 6-8. Figure 6-8

..................Content has been hidden....................

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