CodeIgniter and Ajax using jQuery Creating effective and usable Web 2.0 interfaces Skill Level: Intermediate Kevin Howard Goldberg CTO imagistic 11 May 2010 Discover how easy it is to improve the usability of your CodeIgniter applications using jQuery. By leveraging the power of CodeIgniter's MVC-based framework and jQuery's support for Asynchronous JavaScript and XML (Ajax) interaction, learn how to quickly and efficiently create more effective UIs. CodeIgniter is a popular, lightweight, open source framework written in PHP and based on the Model-View-Controller (MVC) architectural pattern. jQuery is a fast, lightweight, open source JavaScript library designed to simplify manipulating HTML pages and Ajax interactions. When used together, they form a powerful foundation for rapidly developing usable Web sites and applications. Try a no-charge version of DB2 Express 9 database server DB2Express-Cisdesignedtobeupandrunninginminutes,is easy-to-useandembed,includesself-managementfeatures,and embodiesallofthecorecapabilitiesofDB2forLinux,UNIX,and WindowssuchaspureXML™.DB2Express-Coffersthesamecore dataserverbasefeaturesasotherDB2Expresseditionsand providesasolidbasetobuildanddeployapplicationsdeveloped usingC/C++,Java™,.NET®,PHP,RubyonRails,Pythonand otherprogramminglanguages. This article shows how to integrate these two systems into a single framework and how to use jQuery to improve the UI of an existing Web application. It assumes that you have both CodeIgniter version 1.7.2 or later and MySQL version 4.1 or later CodeIgniterandAjaxusingjQuery Trademarks ©CopyrightIBMCorporation2010.Allrightsreserved. Page1of18 developerWorks® ibm.com/developerWorks installed, and that you have a good working knowledge of both. In addition, you need the jQuery library version 1.4.2 or later. If you're new to CodeIgniter or need a quick refresher, see Resources for links to more information. Web 2.0: Ajax using jQuery One of the most important aspects of Web 2.0 is that a user's experience is more like that of a desktop application than a Web site. Specifically, interaction with the Web page doesn't require "visual communication" with the Web server—for example, click Submit, wait for the Web server to process the submitted information, then the entire Web page refreshes with updated content. Instead, only the content that needs to change is updated, while the rest of the page remains in place. This "submission-less" process works through the use of Ajax, which lets Web developers transmit information between a Web client (browser) and a Web server without requiring page refreshes. Better yet, this information transfer can be triggered without any direct user intervention. When a Web page uses Ajax, it is asynchronously sending data to and receiving data from a Web server. This transmitted data is plain text and, therefore, can be in a number of different formats, such as XML, HTML, JSON, or just plain text. The actual transmission of the data is executed using JavaScript and an API called XMLHttpRequest, and that's where jQuery enters the picture. The jQuery library has greatly simplified the process of using Ajax. And not only is using Ajax easier, but displaying the updated data is easier, too. (If you've ever tried traversing the HTML DOM with JavaScript, you'll really appreciate just how much easier it is!) The existing CodeIgniter application To demonstrate the power and simplicity of using jQuery with CodeIgniter, this article guides you through improving the UI of an existing Web application. This application was designed to help a teacher manage class activities and each parent's participation therein. The teacher first selects class activities from a list of board-approved options and schedules a date for each. Then, parents register on the site and enter the contact information for their child (or children). They can then view the list of class activities and select their level of participation (purchase supplies, help prepare, assist, or serve as an activity leader). Note: This system could easily be applied to support a kids' sports team, YMCA group, and the like. For this article, the application's database has been loaded with about 100 events, 5 parents, and 1 teacher. The parents' user names are parent1, parent2, . . . parent5. CodeIgniterandAjaxusingjQuery Trademarks ©CopyrightIBMCorporation2010.Allrightsreserved. Page2of18 ibm.com/developerWorks developerWorks® The teacher's user name is teacher, and all the passwords are ibm. You'll want to download the Web application and database and set them up on a Web server to follow along. The application requires that the CodeIgniter framework be at the root of the server. Setting up jQuery To start using jQuery, first download the library (see Resources for a link). Two files are offered for each version release: an uncompressed file and a "minified" file (this highly compressed version is faster to load but impossible to trace if you're so inclined). I recommend using the uncompressed version for development and the minified version for production. Next, place the jQuery library file in the .assets/js folder at the root of your Web server. Then, create a new file in the same folder called global.js, as shown in Listing 1. This is where you'll place the JavaScript code for the entire application. Listing 1. "Hello World" using jQuery /* Global JavaScript File for working with jQuery library */ // execute when the HTML file's (document object model: DOM) has loaded $(document).ready(function() { alert ('Hello World'); }); For this article—and most applications—much of the jQuery code will reside inside the $(document).ready() function. This function is automatically triggered only after the HTML file's DOM has finished loading. For the application to load both of these files, edit the ./system/application/views/template.php file, as seen in Listing 2. Listing 2. Loading jQuery and the global JavaScript file <head> <meta http-equiv="content-type" content="text/html; charset=UTF-8" /> <link href="/assets/css/screen.css" rel="stylesheet" type="text/css" /> <!-- the following two lines load the jQuery library and JavaScript files --> <script src="/assets/js/jquery-1.4.2.js" type="text/javascript"></script> <script src="/assets/js/global.js" type="text/javascript"></script> <title><?php echo $title;?></title> </head> CodeIgniterandAjaxusingjQuery Trademarks ©CopyrightIBMCorporation2010.Allrightsreserved. Page3of18 developerWorks® ibm.com/developerWorks Now, navigate to the index page of the Web site. When the page loads, a JavaScript alert will say "Hello World." Using jQuery and Ajax with CodeIgniter With the jQuery library and global.js file in place, you're ready to start improving the application's interface. If you haven't already, take a few minutes to log in as a parent and a teacher to become familiar with how activities work in the system. Automatic username validation The first place you'll improve the UI is on the Registration page. Currently, the validation for whether a username has been taken is done after the user submits the page. However, with Ajax, you can run the server-side validation and return the results without requiring any page submission. To do so, bind code to the username field's onblur() event, which is triggered when the user's cursor leaves the field. Listing 3 shows the updated global.js file. Listing 3. Checking whether a username is already registered /* Global JavaScript File for working with jQuery library */ // execute when the HTML file's (document object model: DOM) has loaded $(document).ready(function() { /* USERNAME VALIDATION */ // use element id=username // bind our function to the element's onblur event $('#username').blur(function() { // get the value from the username field var username = $('#username').val(); // Ajax request sent to the CodeIgniter controller "ajax" method "username_taken" // post the username field's value $.post('/index.php/ajax/username_taken', { 'username':username }, // when the Web server responds to the request function(result) { // clear any message that may have already been written $('#bad_username').replaceWith(''); // if the result is TRUE write a message to the page if (result) { $('#username').after('<div id="bad_username" style="color:red;">' + '<p>(That Username is already taken. Please choose another.)</p></div>'); } } ); }); }); CodeIgniterandAjaxusingjQuery Trademarks ©CopyrightIBMCorporation2010.Allrightsreserved. Page4of18 ibm.com/developerWorks developerWorks® The $ in jQuery InjQuery,thedollarsign($)isactuallyanaliasusedtocreatenew jQueryobjects.Therefore,theJavaScriptvariablethisisdifferent fromthejQueryobject$(this),whichiscreatedfromthe JavaScriptvariable.Theuseofthe$canbeespeciallyconfusing forPHPdeveloperswhoareaccustomedtoprecedingvariables witha$.Ifyourunintoproblemswithyourcode,makesureyou checkyouruseofthe$. This code creates a jQuery object using the DOM element with an ID of username. It then calls the jQuery blur() method, which binds a function to the onblur() event for the username field. This function posts the value in the username field, using Ajax, to the CodeIgniter controller named ajax and its method, username_taken. Next, it clears any existing error message and, based on the result of the Ajax post, displays or does not display an error message. Next, in ./system/application/controllers, create a file called ajax.php, which is referred to by the jQuery Ajax .post() method. Listing 4 shows the source code for this controller. (Note that there is nothing special about naming the controller ajax. It could be called anything as long as the URL in the .post() method refers to the correct controller.) Listing 4. The CodeIgniter controller that processes Ajax requests <?php class Ajax extends Controller { public function Ajax() { parent::Controller(); } public function username_taken() { $this->load->model('MUser', '', TRUE); $username = trim($_POST['username']); // if the username exists return a 1 indicating true if ($this->MUser->username_exists($username)) { echo '1'; } } } /* End of file ajax.php */ /* Location: ./system/application/controllers/ajax.php */ Notice that the username_taken() method does not return a value. Rather, it echoes its response, and this is an important point. The jQuery Ajax request posts data to a Web page and uses the resulting page data; it's not programmatically touching the method itself. CodeIgniterandAjaxusingjQuery Trademarks ©CopyrightIBMCorporation2010.Allrightsreserved. Page5of18 developerWorks® ibm.com/developerWorks Now, your first Ajax function is complete. Navigate to the Registration page and register with any user name that is already taken to see the appropriate error message. Interestingly enough, there's an unintentional result of the jQuery code as written. The Ajax function is bound to fields with an ID of username, which includes the username field on the Login page. Listing 5 shows how to modify the jQuery object so that it only binds to the username field within the registration_form form. Listing 5. Qualifying the correct username field /* USERNAME VALIDATION */ // use element id=username within the element id=registration_form // bind our function to the element's onblur event $('#registration_form').find('#username').blur(function() { Seamless status update and storage The next UI improvement is on the Class Activity Listing page. For a parent to indicate his or her participation for a particular activity, the parent clicks the appropriate radio button, then clicks the save link to submit the page. To improve the UI, remove the requirement to click the save link and, therefore, the need to submit the page at all. First, add the following code to the global.js file. It's split across Listing 6, Listing 7, and Listing 8 to more easily describe the process. Listing 6. Using jQuery to get related element values /* AUTOSAVE PARTICIPATION */ // use input element name=participation_type_id and type=radio // bind our function to the element's onclick event $('input[name=participation_type_id]:radio').click(function() { var participation_type_id = this.value; // create global variables for use below var class_activity_id, user_id; // get the form's two hidden input elements // each is a sibling of the parent of the clicked radio button // store their values in the global variables var hidden_elements = $(this).parent().siblings('input:hidden'); $(hidden_elements).map(function() { if (this.name == 'class_activity_id') { class_activity_id = this.value; } if (this.name == 'user_id') { user_id = this.value; } }); jQuery and the DOM hierarchy CodeIgniterandAjaxusingjQuery Trademarks ©CopyrightIBMCorporation2010.Allrightsreserved. Page6of18 ibm.com/developerWorks developerWorks® AlthoughjQueryhidesalotofthecomplexityofnavigatingthe DOM,itisnotapanacea.Specifically,youstillneedabasic understandingofthehierarchicalrelationshipoftheelementsin yourdocumenttousejQueryeffectively. This function is bound to the onclick() event of any radio button named participation_type_id. It gets the value of the clicked radio button. Then, it uses a set of chained methods to return the hidden form elements. The map() method passes each element through its function, retrieving the class_activity_id and user_id values. Having identified the values required to set a parent's participation, the code executes an Ajax request to save this information, as seen in Listing 7. The server's response to this request does not echo any data, so the jQuery response function is empty (and could actually have been removed). Listing 7. Posting an Ajax request to CodeIgniter // Ajax request to CodeIgniter controller "ajax" method "update_user_participation" // post the user_id, class_activity_id and participation_type_id fields' values $.post('/index.php/ajax/update_user_participation', { 'user_id':user_id, 'class_activity_id':class_activity_id, 'participation_type_id':participation_type_id }, // when the Web server responds to the request function(result) { } ); Finally, the text next to the radio buttons is changed to the appropriate color using the jQuery next() method to target each string. This code is shown in Listing 8. Listing 8. Dynamically changing the color of the radio button text // set the text next to the clicked radio button to red $(this).next().css("color", "red"); // set the text next to the remaining radio buttons to black var other_r_buttons = $(this).siblings('input[name=participation_type_id]:radio'); $(other_r_buttons).map(function() { $(this).next().css("color", "black"); }); }); Now that the jQuery code has been written, you need to create the update_user_participation() method in the ajax controller, as seen in Listing 9. Listing 9. Processing user participation in CodeIgniter CodeIgniterandAjaxusingjQuery Trademarks ©CopyrightIBMCorporation2010.Allrightsreserved. Page7of18 developerWorks® ibm.com/developerWorks public function update_user_participation() { $this->load->model('MActivity', '', TRUE); $this->MActivity->set_user_participation($_POST); } This method uses the set_user_participation() method already found in the MActivity model, which takes variables posted by the Ajax request. Finally, comment out the save link in ./system/application/controller/activity.php, as shown in Listing 10. Listing 10. Removing the unnecessary save link '<span style="style="white-space: nowrap;">'. $participation_type_buttons.' '. /* the save link is no longer needed '<a href="" onclick="document.forms[\'form_'.$activity->id.'\'].submit(); return false;">save</a>'. */ '</span>'. '</form>'; You can now change a parent's participation, and it will be automatically saved without a page refresh. Autosuggest input field One of the most effective and widespread uses of Ajax is the autosuggest or autocompletion functionality. Log in as a teacher, and click Manage Class Activities. To add an activity, users must scroll down the long list of possible activities. To improve the UI, add an input field bound to an autosuggest function at the top of the ./system/application/views/activity_master_listing.php file, as seen in Listing 11. Doing so allows the teacher to more easily select from all the unscheduled activities. Listing 11. Adding an autosuggest input field <div id="select_anchor"> <a href="" onclick="$('#select_anchor').hide(100); $('#select_activity').show(100); return false;"> Select an unscheduled Activity to add >></a> <br /><br /> </div> <div id="select_activity" class="requested_activity" style="display:none;"> <table> <caption> Select an unscheduled Activity</caption> <tr class="odd_row_add"> <td> Begin by typing a few letters of an activity name<br /> then select from the resulting list<br /> <br /> <input type="text" value="" id="class_activity" CodeIgniterandAjaxusingjQuery Trademarks ©CopyrightIBMCorporation2010.Allrightsreserved. Page8of18 ibm.com/developerWorks developerWorks® onkeyup="autosuggest(this.value);" class="autosuggest_input" /> <div class="autosuggest" id="autosuggest_list"></div> </td> </tr> </table> </div> Take note of the two jQuery objects and methods bound to the JavaScript onclick() event. Remember that jQuery is simply a JavaScript library and can seamlessly interact with JavaScript throughout an application, not just within the $(document).ready() function. Next, outside the $(document).ready() function in the global.js file, implement the following JavaScript function. It is bound to the onkeyup() event of the class_activity input field. The source code is shown in Listing 12. Listing 12. Implementing autosuggest with jQuery /* AUTOSUGGEST SEARCH */ // triggered by input field onkeyup function autosuggest(str){ // if there's no text to search, hide the list div if (str.length == 0) { $('#autosuggest_list').fadeOut(500); } else { // first show the loading animation $('#class_activity').addClass('loading'); // Ajax request to CodeIgniter controller "ajax" method "autosuggest" // post the str parameter value $.post('/index.php/ajax/autosuggest', { 'str':str }, function(result) { // if there is a result, fill the list div, fade it in // then remove the loading animation if(result) { $('#autosuggest_list').html(result); $('#autosuggest_list').fadeIn(500); $('#class_activity').removeClass('loading'); } }); } } Notice that although this function is not within the $(document).ready() function, it still uses jQuery objects and methods. Its jQuery .post() method is calling the Ajax controller's autosuggest() method, which echoes an unordered list of autosuggest results. Listing 13 shows this code. Listing 13. Retrieving and echoing autosuggest results public function autosuggest() { // escapes single and double quotes $str = addslashes($_POST['str']); CodeIgniterandAjaxusingjQuery Trademarks ©CopyrightIBMCorporation2010.Allrightsreserved. Page9of18 developerWorks® ibm.com/developerWorks $this->load->model('MActivity', '', TRUE); $unscheduled_activities_qry = $this->MActivity->find_unscheduled_activities($str); // echo a list where each li has a set_activity function bound to its onclick() event echo '<ul>'; foreach ($unscheduled_activities_qry->result() as $activity) { echo '<li onclick="set_activity(\''.addslashes($activity->name).'\''; echo ', '.$activity->id.');">'.$activity->name.'</li>'; } echo '</ul>'; } Next, add the find_unscheduled_activities() method to return the autosuggest results from the database. Listing 14 contains the code from ./system/application/models/mactivity.php. Listing 14. Querying the database for unscheduled activities // Finds all unscheduled activities that match the passed string function find_unscheduled_activities($str) { $this->db->select('id, name FROM master_activity WHERE name LIKE \''.$str.'%\' AND id NOT IN (SELECT master_activity_id FROM class_activity) ORDER BY name', FALSE); return $this->db->get(); } You'll also want to style the autosuggest <div> and list so that the UI is clearer and the list elements are clickable. The styles added to ./assets/css/screen.css are shown in Listing 15. Listing 15. Making the autosuggest list clear and clickable /***************/ /* Autosuggest */ .autosuggest { border:1px solid #000000; display:none; overflow:hidden; padding:0px; position:absolute; width:200px; z-index:1; } .autosuggest ul li { background-color:#FFFFFF; cursor:pointer; display:block; list-style:none; padding:5px; white-space:nowrap; } .autosuggest ul li:hover { CodeIgniterandAjaxusingjQuery Trademarks ©CopyrightIBMCorporation2010.Allrightsreserved. Page10of18
Description: