#native_company# #native_desc#
#native_cta#

Ajax Edit in Place Using Prototype

By Ben Robinson
on December 7, 2006

In this article I will show you how to use the popular prototype JavaScript include file to use an Ajax technique called edit in place, but first, let’s explain the technique.?? Edit in place, like many other Ajax functions, uses the XmlHttpRequest Object to call an external page to send and receive information.?? If you are not familiar with Ajax technologies, there is a great 2-part tutorial on this site located here.
First of all, let me introduce the architecture I have for this application.?? Since we are using a database and PHP to handle the data exchange layer, let’s start there:

The first layer is the MysqlDB class, which upon instantiation, contains basic methods for performing operations on the mysql database. Since this article intends to focus on the edit in place technique, and namely Ajax, we will be focusing on the brains.php, as well as the Ajax_Interface PHP class (which, as you may have guessed, inherits the MysqlDB class).??From a top level approach, the index.php gets called, which has an include to the brains.php page, as well as the functions.js and prototype.js files.?? The brains.php page, when included from index.php, instantiates the Ajax Interface class, making all the methods available to the index.php page.
Let’s continue by looking at the two PHP files, Ajax_Interface.php and brains.php.?? While I did not need to use classes for this application, I think it is a good place to start for Ajax type uses, as extensibility is the name of the game when you’re dealing with complex operations like xml retrieval, dynamic outputs, etc.?? Take a look at the method print_records(). This sets up the page on load to show what’s currently in the database. Take special note of the output:
function print_records() {
??????????????????????????????????????????????
?????????????????????????????????????????????? $this->get_records();
?????????????????????????????????????????????? $products = $this->rs;??
?????????????????????????????????????????????? $id = $products['product_id'];
?????????????????????????????????????????????? $name = $products['name'];

?????????????????????????????????????????????? do ???????????? {
??????????????????????????????????????????????????????????????????????
?????????????????????????????????????????????????????????????????????? $id = $products['product_id'];
?????????????????????????????????????????????????????????????????????? $name = $products['name'];
?????????????????????????????????????????????????????????????????????? $field = 'field_'.$id;
??????????????????????????????????????????????????????????????????????
echo '<input type="text" name"?" id="'.$id.'" value="'.$name.'" 
onClick="changeClass(''.$id.'', 'show')" 
onBlur="changeClass(''.$id.'', 'hide')" 
onKeyPress="checkLength(this)" class="text" maxlength="60" readonly />';
?????????????????????? echo "n";
??????????????????????????????????????????????????????????????????????
?????????????????????????????????????????????????????????????????????? } while ($products = $this->fetch_array($this->result));??????????????????????
?????????????????????? }

This is looped inside the query result to output a div for each entry. Take special note of the onClick and onBlur events contained in the looped echo statement.?? These are the core of the technique we will be using.?? The onClick event kicks off the function below to change the text field to an input field (changeClass).?? The onBlur event kicks off the update process (using the same function.)?? Let’s take a look at the changeClass function:
function changeClass(id, action) {
???????????? if (action=="hide") {
?????????????????????? $(id).className = "text";
?????????????????????? save(id);
???????????? } else {
???????????? ???????? $('status').innerHTML ='';
?????????????????????? $(id).className = "input";
?????????????????????????????????????????????????????????????????????? $(id).readOnly = false;
???????????? }
}

This is the core of the index page functionality.?? Clicking the text item changes the CSS class to reveal an input field. Leaving the focus of that input field (onBlur event) triggers the same function but swaps it back to a non-editable text field (with the input entered only temporary though, does not write to the db).?? Note that??the save() function is called during the onBlur event, which kicks off the Ajax part of our code. Here is the save function:
function save(id){

?????????????????????? var new_content???????????????????? =?? $F(id);
?????????????????????? var success???????????? = function(t){??
?????????????????????????????????????????????? $('ddiv').innerHTML = t.responseText + 'supposed to be here';
?????????????????????? }
?????????????????????? var failure???????????????? = function(t){ alert('update failed'); }
?? ?????????????????? var url = 'http://localhost/ajax/prototype/include/brains.php';
?????????????????????? var pars = 'edit=go&content='+new_content+'&product_id='+id;

?????????????????????? var myAjax = new Ajax.Request(
?????????????????????????????????????????????????????????????????????? url,
?????????????????????????????????????????????????????????????????????? {
?????????????????????????????????????????????????????????????????????????????????????????????? method: 'get',
?????????????????????????????????????????????????????????????????????????????????????????????? parameters: pars,
?????????????????????????????????????????????????????????????????????????????????????????????? onComplete: update_page
?????????????????????????????????????????????????????????????????????? }?????????????????? );
?????????????????????? }

You’ll Notice the $() and $F() functions.?? These are shortcut functions offered by prototype that perform basic DOM functions for you (like document.getElementById).?? For more information on these and other ajax functions, see the documentation link for prototype at the end of this article.
Now take a look at the variables url and pars. They are the url that is called and the parameters passed to it.?? A new Ajax request (prototype function) is then initiated, with the onComplete event calling the update page function. We’ll take a look at that shortly, but let’s get a peek at how the PHP handles the GET request sent by the save() function:
if ($_GET['edit'] == 'go')?? {
??????????????????????
?????????????????????? $result = $ai->save();
?????????????????????? header('Content-Type: text/xml');
?????????????????????? // generate XML header
?????????????????????? echo '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>';
?????????????????????? echo "<response>";
?????????????????????? echo $result;
?????????????????????? echo "</response>";
?????????????????????? }

When an edit request is received through the?? $_GET method, this triggers a call to the $ai->save(); method.?? This method simply takes the active $_GET items from the query string and updates the database row with the information passed from the XmlHTTPRequest Object, then calls the basic query method to output the updated result from the database as xml.?? You??ll see that I output only the ‘guts’ of the xml in the save method in the Ajax_interface class:
function save() {
??????????????????????????????????
?????????????????????????????????????????????????????????? ??$query = 'update product SET name="'.$_GET['content'].'"
?????????????????????????????????????????????????????????? ?? where product_id = '.$_GET['product_id'].' LIMIT 1';
?????????????????????????????????????????????????????????? ??
?????????????????????????????????????????????????????????? ??$this->query($query);
?????????????????????????????????????????????????????????? ??$output = '<pID>'.$_GET['product_id'].'</pID>.
?????????????????????????????????????????????????????????? ??<value>'.$_GET['content'].'</value>';
?????????????????????????????????????????????????????????? ??
?????????????????????????????????????????????????????????? ??return $output;
?????????????????????????????????? }

-and wrap it with the header in brains.php:
echo '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>';

It’s totally up to your coding style how you want to do this. And lastly, here is how I update the div to contain the updated information from the database:
function update_page(originalRequest) {
??????????????????????
?????????????????????? var rXML = originalRequest.responseXML;
?????????????????????? var pIDs = rXML.getElementsByTagName('pID').item(0);
?????????????????????? var pID = pIDs.firstChild.data;
?????????????????????? //alert('i have ' + pID + ' to update');
?????????????????????? var values = rXML.getElementsByTagName('value').item(0);
?????????????????????? var value = values.firstChild.data;
?????????????????????? //alert('i have ' + value + 'as a return value');
??????????????????????
?????????????????????? $(pID).innerHTML = value;??????????
?????????????????????? $('status').innerHTML = 'update complete';
??????????????????????????????????????????????
?????????????????????? }

What I’m doing in the update_page function is parsing the returned xml by node name. I’m also updating a div that on the first page load has nothing in it (div select), so you will see the ‘update complete’ text appear below your edit table when the update is complete. Inside this function as well I update the individual div to contain the information in the database. To verify this is working, since the css actually stores your new content without the Ajax call, you can refresh the page.
This is the core of how the edit in place works.?? Here are some suggestions for extensibility and/or exercises with this example:
  1. Modify the save() method in the AjaxInterface class to output dynamic xml for any number of records or columns in a database table. (in turn, how would the update_page() and save() JavaScript functions be modified to send and receive a varying amount of data? )
  2. Modify the layout style of index.php to handle an entire address record from the db, not just one entry, to show the usage of the modifications from step 1 above.
In conclusion, we have seen a simple, layered implementation of the edit-in-place Ajax technique. This could be extended to various states of functionality, such as the ability to only edit individual fields of a record, or to have a record available for editing w/o refreshing a page.?? You could add a control to load new records from a selection box, which could populate your editing area as well.?? Take a look at the documentation on prototype, and perhaps the books mentioned below–there’s much more to learn about these types of techniques for your projects.

You can grab the code for this article below.

Useful Links

Special Thanks to Dave Bergschneider of Truimage Productions for providing the css and dynamic input field javascript functions.

About the Author
Ben Robinson is an open source coder who enjoys working on dynamic web applications, especially in PHP and mysql, in the L.A.M.P. environment. His website is TierraLogic Systems (http://www.tierralogic.com/) if you??d like to pay him a visit.

Download: ajax_edit_in_place.zip