Figure 12-4. Basic ticket-manager interface
The look of the ticket application is controlled by the Ticket.css CSS file. There is no need to cover the CSS in detail because it doesn't do anything AJAX-specific. The one item to note is that it does specify default display types for a number of CSS classes; this allows us to have all of the various sections of the site hidden by default, so you don't see them until they have been selected. Now that you've seen what we're building, let's look at it in detail, starting with the page that pulls it all together (see Listing 12-7).
Listing 12-7. index.php
The rest of the file builds a basic user interface; this includes a navigation menu, a simple front page, and a sidebar. On line 58, we add an onload handler to the page, which calls a setup method that exists in the shared Ticket.js file. Next, the page defines the header DIV (lines 6081). This DIV contains the application's name (line 61) and its navigation links (lines 6280). The navigation links call the setSection function to change which section is selected, and they use a CSS class to mark which ones should be shown when the user is logged in. Any element on the page with a CSS class of loggedIn will be shown only when the user is logged in. The hiding and showing of these elements is handled in the login component.
Lines 85102 define the main body of the application. Most of this is just including the HTML for the given modules, but the default view is created right on this page because it is so simple. It contains a form (lines 9094) that calls the viewTicketForm function when submitted. This function is defined in the ticket component. The page finishes by defining a sidebar DIV (lines 104106) that includes the login component. Having the login on the sidebar allows the user to log in or out at any time.
Listing 12-8. Ticket.js
Lines 1015 define the setup function. This function is called by the document's load event and gives each module in the application a way to run some code at load time without first having to register an event handler. This is done by looping over the functions in the app.setup array (lines 1113) and running them. Because the modules are included before the onload event runs, they can add functions to this array as they are included. The setup finishes by selecting the front section of the application.
The next item defined in Ticket.js is the selectSection function. This function acts as the main controller of the application, selecting which section will be shown. It takes a single parameter, which is the ID of the section to be shown. First, the function checks whether the user is logged in (lines 1925). Only the front, ticket, and register sections can be used without logging in. By doing this check, we prevent errors from taking the user to a section that won't work. Next the function selects all the sections within the body; each section is marked with section class, so we use the byClass function to do this. Then we loop over the sections; if the ID matches the section we passed in (line 29), we show it (line 30) and run its onDisplay method if it exists. Otherwise, we hide the section (line 27). Running the onDisplay method gives the section a way to run code as the section is displayed. This gives it the opportunity to update data or clear out old data.
Lines 4149 define the setMessage function. This is a simple function that allows us to give the user a success or failure message that will be shown for three seconds and then fade off the screen. The first parameter to the function is the DOM element to fade; the second parameter is the message to use. Depending on your design, you can always use a single message target so that the first parameter isn't needed, but in this case, the flexibility is needed, so the same code can be used for login messages. The fading out of the message is performed by the use of a scriptaculous effect (line 48), which is applied inside a setTimeout call; the delay on running the timeout function is in milliseconds, so the value of 3000 equals 3 seconds.
When combined with an alert color, such as yellow or red, notification messages that appear and then fade away after a short period can be very successful in AJAX applications. The disappearing message keeps the alerts timely, and it is needed so that the user can tell which action the message applied to, because a page reload won't be clearing it. The biggest downside of this approach is that the user can lose useful information if it disappears before he or she has read the message. One approach to get around this problem is to add a message history to the application. Instead of being removed completely, the message is added to the message history after three seconds. An example message is shown in Figure 12-5.
Figure 12-5. Message that will fade away after three seconds
The positionOver function is used to position a passed-in element over its parent element. This is used when you want to cover an element, such as a form, with an opaque message while it is being AJAX processed. This is a useful loading technique because it will prevent the form from being clicked while you are processing the last request it sent. To use the function, follow these steps:
Once the element has been added, you run positionOver(element), and the element is positioned to hide all the elements to which it was added. You can show the parent by removing the element from the DOM or setting its display property to none. Scriptaculous effects, such as fade, are a good way to remove this element. The positioning elements of this function can be achieved using CSS on some browsers, but if you want it to work everywhere, size the element with CSS.
Another possible approach to preventing a form from being submitted while its results are loading is to disable its form submission button. This is often accompanied with changing the label on the button to a loading message. Both approaches provide good feedback to the users, but positioning a DIV over the form gives the developer the ability to display a larger loading message. Figure 12-6 shows an example of positioning an element over a form as a loading message; Figure 12-7 shows an example of disabling the form submission button.
Figure 12-6. Providing form-loading status by using the positionOver function
Figure 12-7. Providing form-loading status by disabling the form submission button
The last two functions in Ticket.js work in concert to allow us to dynamically update tables using a template approach. The first function, buildTable (lines 6375), takes an array of data and the ID of the table to update. It deletes all current rows from the table's body (if you put your headers in the <thead> tag, they will not be deleted) on lines 6567. Then it loops over all the data (lines 6974), creating a new row by cloning a template node and then runing updateRow using that row's data.
This template is used to build each row of output. After replacing its tokens with data that has a matching ticket_id and title property, you will produce a table row like this:
<tr><td>#4</td><td>A Sample Ticket</td></tr>
This same replacement approach could be used against many other types of DOM elements; in fact, the most problematic elements for this approach are tables because they don't update properly when working with the innerHTML of their individual rows.