The Search Application
Here's an example that shows how to apply Dojo's history features to the search application problem mentioned earlier.
Figure 1 shows the Search application directory structure:
 | |
Figure 1. Search Application Directory Structure: The sample search application uses these folders. |
|
 | |
Figure 2. Simple Search Form: Users enter some search text and click the Search button, which sends a background request to the server. |
|
|
The main page (
searchMain.jsp, from which users can trigger a search) resides in the application's root folder (see
Figure 2). The
dojo folder contains the standard Dojo files such as
dojo.js. The script folder contains special JavaScript files for the search application, including
HistoryTracker.js, which defines the application state implementation, and
dojoUtility.js, which defines JavaScript utility functions such as the
body onload handler function and the function for firing the search.
Author's Note: This sample application uses JSP, but the example would work equally well with any server-side technology (PHP, ASP, etc.). |
Here's the HTML for the
<form> tag shown in
Figure 2:
<form name="searchForm" action="#">
<input type="text" name="searchTextElement"></input>
<input type="button" name="Search" value="Search"
onclick="performSearch(
this.form.searchTextElement.value);"></input>
</form>
Note that the HTML uses the input type of "button" (rather than using "submit") to avoid automatic form submission. The goal is to send an asynchronous background request to fetch the search result. Therefore, the button's
onclick event calls the
performSearch() method, which triggers search operation.
The
performSearch() function code shown below is in a separate
dojoUtility.js file:
function performSearch(searchTxt, pageNumber) {
var bindUrl = "/dojoapp/doSearch.jsp";
if(searchTxt) {
bindUrl += "?searchTxt=" + searchTxt ;
if(pageNumber) {
bindUrl += "&pageNumber=" + pageNumber;
}
dojo.io.bind({
url: bindUrl,
load: function(type, data, evt){
dojo.undo.browser.addToHistory(
new HistoryTracker(data, searchTxt,
pageNumber, "searchContent"));
dojo.byId("searchContent").innerHTML = data;
}
 | |
Figure 3. Dummy Search Result: The elements of the search results appear at the bottom of the page. |
});
}
}
The
performSearch() method accepts two parameters—
searchTxt and
pageNumber—that correspond respectively to the search text entered in the text box and the search page to which the user wants to navigate. The
performSearch() function sends an asynchronous request to
doSearch.jsp, which retrieves the search result. You can see a dummy search result at the bottom of
Figure 3:
The code that produces the search results is:
You have searched for:
<%= request.getParameter("searchTxt") %>
<br/>
Showing page number:
<%= (request.getParameter(
"pageNumber") == null) ?
"1" : request.getParameter(
"pageNumber") %>
<br/>
<a href="javascript:performSearch(
'<%= request.getParameter("searchTxt") %>',
'<%= (request.getParameter("pageNumber") == null) ? "2" :
(Integer.parseInt(request.getParameter(
"pageNumber") ) + 1) %>');">View next set of results</a>
This is a dummy page printing only the page number and search text. The doSearch.jsp page provides the anchor tag for fetching the next set of search results. The
javascript:performSearch() function is the
href location for that tag. It takes the search text and the next page number as parameter.
The
performSearch() function uses the HistoryTracker object. You register the application state change by calling
dojo.undo.browser.addToHistory(). To make that work, though, you need to remember to register the initial application state, which usually happens in the
body onload event:
dojo.addOnLoad(handleBodyLoad);
The
handleBodyLoad() function is defined as:
function handleBodyLoad() {
var state = new HistoryTracker(null, null, null, "searchContent");
dojo.undo.browser.setInitialState(state);
handleUrlHash();
}
The
handleBodyLoad() function creates a state object that has null values for both
searchTxt and
pageNumber. Then it registers the initial application state by passing that state object in the line
dojo.undo.browser.setInitialState(state). Finally, it calls
handleUrlHash(), which parses the URL hash and restores application state depending on the value of
hash:
function handleUrlHash() {
var urlHash = location.hash.substring(1);
var searchText, pageNumber;
if(urlHash && urlHash != '') {
var hashParams = urlHash.split(";");
for (i = 0; i < hashParams.length; i++) {
var temp = hashParams[i].split("=");
if(temp && temp.length > 0) {
switch(temp[0]) {
case 'searchTxt':
searchText = temp[1];
break;
case 'pageNumber':
pageNumber = temp[1];
break;
}
}
}
}
if(searchText) {
if(pageNumber) {
performSearch(searchText, pageNumber);
}
else {
performSearch(searchText, 1);
}
}
}
 | |
Figure 4. AJAX Search Results: The search results appear on the same page as the search form. |
The
handleUrlHash() method parses the URL hash to retrieve the search text and page number and then calls
performSearch() with the interpreted search text and page number. The
performSearch() method shown earlier is responsible for firing the search. It calls
doSearch.jsp using
searchText and
pageNmuber as parameters, and then displays the search results.
That completes the search application; now you can launch the search page by browsing to the URL
http://yourdomain/searchMain.jsp.
When you see the main form, enter some search text (for example, "What is Dojo") and you'll see the search results appear on the same page, (see
Figure 4).
Although the page doesn't change, you'll see that the application appended a URL hash to the original URL, for example:
http://localhost:8080/dojoapp/searchMain.jsp#searchTxt=
What%20is%20Dojo;pageNumber=1
The URL hash contains semicolon-separated values for
searchTxt and
pageNumber. If you click the "View next set of results" link, you'll see a second search results page.
As you can see, Dojo can make your AJAX applications respect bookmarks and the Back and Forward buttons. As illustrated in the sample application, you assign a distinct URL hash to each page, which updates the browser history. Using this technique, the Back and Forward buttons work correctly, so users can easily bookmark any search page. The
downloadable code contains the ready-to-use sample application described in this article. You can reuse the ideas it exemplifies as the basis of your own navigation-aware AJAX applications.