CHAPTER 7 ■ ENHANCING THE USER INTERFACE WITH JQUERY 261 You may have noticed that the modal window appears to flicker right as it’s opened. This happens because fx.initModal() appends the modal window to the body element without hiding it. To correct this, add a call to .hide() in fx.initModal() using the following bold code: // Functions to manipulate the modal window fx = { // Checks for a modal window and returns it, or // else creates a new one and returns that "initModal" : function() { // If no elements are matched, the length // property will be 0 if ( $(".modal-window").length==0 ) { // Creates a div, adds a class, and // appends it to the body tag return $("<div>") .hide() .addClass("modal-window") .appendTo("body"); } else { // Returns the modal window if one // already exists in the DOM return $(".modal-window"); } }, // Adds the window to the markup and fades it in "boxin" : function(data, modal) { // Code omitted for brevity }, // Fades out the window and removes it from the DOM "boxout" : function(event) { // Code omitted for brevity } }; Finally, clicking the Close button does not remove the overlay. To fade out and remove the overlay, simply modify the selector in fx.boxout() to include the class modal-overlay: // Functions to manipulate the modal window fx = { // Checks for a modal window and returns it, or // else creates a new one and returns that "initModal" : function() { // Code omitted for brevity CHAPTER 7 ■ ENHANCING THE USER INTERFACE WITH JQUERY 262 }, // Adds the window to the markup and fades it in "boxin" : function(data, modal) { // Code omitted for brevity }, // Fades out the window and removes it from the DOM "boxout" : function(event) { // If an event was triggered by the element // that called this function, prevents the // default action from firing if ( event!=undefined ) { event.preventDefault(); } // Removes the active class from all links $("a").removeClass("active"); // Fades out the modal window and overlay, // then removes both from the DOM entirely $(".modal-window,.modal-overlay") .fadeOut("slow", function() { $(this).remove(); } ); } }; After making this change, reload http://localhost/ and click an event title. The modal window and overlay will fade in, and clicking either the Close button or the overlay will cause the modal window and overlay to fade out. Summary In this chapter, you learned how to load event data dynamically with jQuery using the progressive enhancement technique. You also learned about event handling, basic effects, and even a little bit about regular expressions. In the next chapter, you’ll continue to add AJAX functionality by making the editing controls work via AJAX, as well. C H A P T E R 8 ■ ■ ■ 263 Editing the Calendar with AJAX and jQuery Now that your app can display event data without a page refresh, you can see the added convenience provided by AJAX in web applications. Historically, one of the biggest pitfalls of using web apps has been the fact that each action, no matter how small, usually required waiting for the page to refresh while the setting was saved. Web apps were convenient when a user needed access to his information on a shared computer, but the slow workflow was usually enough to make users lean toward desktop applications whenever possible. However, with mainstream acceptance and use of AJAX, users can now make changes rapidly without constantly waiting for a page to reload. This makes web apps feel more like desktop apps, which also makes them much more appealing to users. In this chapter, you’ll learn to add scripts that make the editing controls for administrators function smoothly without requiring a page refresh for each action. The only action that will require a page refresh is the login, since that requires changes to the session. ■ Note Before starting the exercises in this chapter, please log in to the calendar application. The default login relies on a username of testuser and a password of admin. Opening the Event Creation Form To start, you’ll modify the script to let administrators add new events without a page refresh. Open init.js and select the button to add new events by its class (admin). Add a click event handler that prevents the default action and (for now) logs a message to confirm it fired properly: jQuery(function($){ var processFile = "assets/inc/ajax.inc.php", fx = { "initModal" : function() { }, "boxin" : function(data, modal) { }, "boxout" : function(event) { } } $("li>a").live("click", function(event){ }); CHAPTER 8 ■ EDITING THE CALENDAR WITH AJAX AND JQUERY 264 // Displays the edit form as a modal window $(".admin").live("click", function(event){ // Prevents the form from submitting event.preventDefault(); // Logs a message to prove the handler was triggered console.log( "Add a New Event button clicked!" ); }); }); ■ Note For the sake of brevity, all unchanged functions have been abbreviated, and comments have been omitted from code samples in this chapter. You can find the code at the book’s Apress page: http://apress.com/book/view/1430228474. Save this code and refresh http://localhost/. Click the Add a New Event button, and you’ll see the following result logged to the console: Add a New Event button clicked! Adding an AJAX Call to Load the Form Next, create a variable to store an action that will be sent to the processing file. You’re loading the editing and creation form, so set the action to event_edit. Now you can call the $.ajax() function. This function will be similar to the script for loading event data into the modal window; in fact, the only difference will be in the data submitted and the way the return value is handled. On a successful load, you hide the form and store a reference to it in the variable form. Next, you check for a modal window using fx.initModal() and fade it in using fx.boxin() with a null first argument. Finally, you append the form to the modal window, fade it in, and assign to it the class edit- form for easy selection later. Add the following bold code to init.js to carry out these steps: jQuery(function($){ var processFile = "assets/inc/ajax.inc.php", fx = { "initModal" : function() { }, "boxin" : function(data, modal) { }, "boxout" : function(event) { } } CHAPTER 8 ■ EDITING THE CALENDAR WITH AJAX AND JQUERY 265 $("li>a").live("click", function(event){ }); // Displays the edit form as a modal window $(".admin").live("click", function(event){ // Prevents the form from submitting event.preventDefault(); // Loads the action for the processing file var action = "edit_event"; // Loads the editing form and displays it $.ajax({ type: "POST", url: processFile, data: "action="+action, success: function(data){ // Hides the form var form = $(data).hide(), // Make sure the modal window exists modal = fx.initModal(); // Call the boxin function to create // the modal overlay and fade it in fx.boxin(null, modal); // Load the form into the window, // fades in the content, and adds // a class to the form form .appendTo(modal) .addClass("edit-form") .fadeIn("slow"); }, error: function(msg){ alert(msg); } }); }); }); Modifying the AJAX Processing File to Load the Form Before the preceding AJAX call will work, you need to modify the ajax.inc.php lookup array. Add a new array element that tells the script to create a new Calendar object, and then call the displayForm() method with the code shown in bold: CHAPTER 8 ■ EDITING THE CALENDAR WITH AJAX AND JQUERY 266 <?php /* * Enable sessions */ session_start(); /* * Include necessary files */ include_once ' / / /sys/config/db-cred.inc.php'; /* * Define constants for config info */ foreach ( $C as $name => $val ) { define($name, $val); } /* * Create a lookup array for form actions */ $actions = array( 'event_view' => array( 'object' => 'Calendar', 'method' => 'displayEvent' ), 'edit_event' => array( 'object' => 'Calendar', 'method' => 'displayForm' ) ); /* * Make sure the anti-CSRF token was passed and that the * requested action exists in the lookup array */ if ( isset($actions[$_POST['action']]) ) { $use_array = $actions[$_POST['action']]; $obj = new $use_array['object']($dbo); /* * Check for an ID and sanitize it if found */ if ( isset($_POST['event_id']) ) { $id = (int) $_POST['event_id']; } CHAPTER 8 ■ EDITING THE CALENDAR WITH AJAX AND JQUERY 267 else { $id = NULL; } echo $obj->$use_array['method']($id); } function __autoload($class_name) { $filename = ' / / /sys/class/class.' . strtolower($class_name) . '.inc.php'; if ( file_exists($filename) ) { include_once $filename; } } ?> Now save the file, load http://localhost/, and click the Add a New Event button. A new modal window will fade in with the edit form inside (see Figure 8-1). CHAPTER 8 ■ EDITING THE CALENDAR WITH AJAX AND JQUERY 268 Figure 8-1. The event creation form loaded in a modal window Making the Cancel Button Behave Like the Close Button You may have noticed that the modal window doesn’t contain a Close button when the form is displayed. However, the modal window does include a Cancel button that will refresh the page when clicked. Instead of adding more buttons to the window, you’re simply going to make the Cancel button call the fx.boxout() method to close the window. To accomplish this, use .live() to bind a click event handler to any link containing the word cancel inside a form with the edit-form class: jQuery(function($){ var processFile = "assets/inc/ajax.inc.php", fx = { "initModal" : function() { }, CHAPTER 8 ■ EDITING THE CALENDAR WITH AJAX AND JQUERY 269 "boxin" : function(data, modal) { }, "boxout" : function(event) { } } $("li>a").live("click", function(event){ }); $(".admin").live("click", function(event){ }); // Make the cancel button on editing forms behave like the // close button and fade out modal windows and overlays $(".edit-form a:contains(cancel)").live("click", function(event){ fx.boxout(event); }); }); Save the file, reload http://localhost/, and click the Add a New Event button. After the modal window has loaded, click the Cancel link in the form. The modal window and overlay will fade out, just as they do when the Close button is clicked. Saving New Events in the Database To make the form work properly, you must now add a click event handler to the Submit button on the form. This handler will prevent the default form submission, then use .serialize() to create a query string from the form inputs. It then uses the serialized data to submit the form via POST to ajax.inc.php. Start by adding a new click handler to any inputs of type submit that exist in a form with the edit- form class. Using .live() ensures that dynamically created inputs will still be targeted by the handler. You can prevent the default action using event.preventDefault(). Do this by inserting the bold code into init.js: jQuery(function($){ var processFile = "assets/inc/ajax.inc.php", fx = { "initModal" : function() { }, "boxin" : function(data, modal) { }, "boxout" : function(event) { } } $("li>a").live("click", function(event){ }); $(".admin").live("click", function(event){ }); // Edits events without reloading $(".edit-form input[type=submit]").live("click", function(event){ // Prevents the default form action from executing event.preventDefault(); // Logs a message to indicate the script is working CHAPTER 8 ■ EDITING THE CALENDAR WITH AJAX AND JQUERY 270 console.log( "Form submission triggered!" ); }); $(".edit-form a:contains(cancel)") .live("click", function(event){ }); }); Next, save and reload the calendar in your browser. Click the Add a New Event button to bring up the modal window, and then click the Create a New Event button to submit the form. This outputs the following result to the console: Form submission triggered! Serializing the Form Data To send the event data to the processing file, you need to convert the data to a query string. Fortunately, jQuery has a built-in method to do this called .serialize(). It will convert form inputs into a string of name-value pairs, separated by an ampersand (&). Modify init.js to serialize the form data by selecting the parent form of the clicked input, and then serialize its data. Next, log the output to the console for now: // Edits events without reloading $(".edit-form input[type=submit]").live("click", function(event){ // Prevents the default form action from executing event.preventDefault(); // Serializes the form data for use with $.ajax() var formData = $(this).parents("form").serialize(); // Logs a message to indicate the script is working console.log( formData ); }); Save the preceding code and bring up the event creation form in your browser. Now enter the following test data: • Event Title: Test Event • Event Start: 2010-01-04 08:00:00 • Event End: 2010-01-04 10:00:00 • Event Description: This is a test description. Click the Create a New Event button to submit the form and output the following to your console (the token value will vary): . WITH AJAX AND JQUERY 266 < ?php /* * Enable sessions */ session_start(); /* * Include necessary files */ include_once ' / / /sys/config/db-cred.inc .php& apos;; /*. enter the following test data: • Event Title: Test Event • Event Start: 201 0-0 1-0 4 08:00:00 • Event End: 201 0-0 1-0 4 10:00:00 • Event Description: This is a test description. Click the Create. window will fade in with the edit form inside (see Figure 8-1 ). CHAPTER 8 ■ EDITING THE CALENDAR WITH AJAX AND JQUERY 268 Figure 8-1 . The event creation form loaded in a modal window Making