August 2008
Developer: Dynamic loading of lists using AJAX in a MVC component
Ajax, Mootools, JSon, and MVC
Written by Mathieu Chauvinc
Requires: Joomla! 1.5.3 or later, Mootools 1.1, PHP5 (or PHP4 with the json library).
Desc: Tutorial on using Ajax and Mootools for a Form in a MVC component. The example used aims at dynamically loading a list of MySQL table fields when a table is selected from another list.
Useful tools: Firebug
Notes:
- JS stands for Javascript
- Words found in bold in the text are references to parts of the code such as methods, variables, ...
- In purple color are the names of the files where code needs to be inserted
Getting started: Understanding the tools
AJAX
Ajax is a methodology which uses existing technologies to enable a website to make asynchronous requests to a server, meaning requests that are being sent without the page reloading. It has great advantages in terms of user-friendliness of websites.Ajax is not a programming language. It uses Javascript + your server + XML (although we will use JSon instead of XML).
Mootools
Mootools is a lightweight but extremely powerful, object-oriented javascript framework. It works on any browser. All the Mootools code we will use could be re-written in "classic" javascript, without the Mootools framework, but that would be a shame.JSon (read Jason)
JSON (JavaScript Object Notation) is a lightweight data-interchange format. It is easy for humans to read and write. It has become the choice data-interchange format for AJAX calls, as it is much easier to parse than XML, both on the server side and the client side. What it does is:- from the client side, take a JS object and transform it into a URL-safe string
- on the server side rebuild the object from the string (the object is then a regular PHP object)
- when the response to the request is being sent, encode the PHP object into a string
- on the client side, decode the string into a regular JS object
MVC
The Joomla! model-view-component is well documented and plenty of tutorials exist. We'll assume that you have a working MVC back-end component called com_mycomponent.
Our example
A large number of applications can be imagined for loading data using Ajax requests. These include:- selecting a country and dynamically loading the list of states / cities
- dynamically loading a list of users using filters (such as type, part of the username, etc...)
- dynamically testing that a username / an email address is not yet in use before even submitting the form
- displaying the number of results that will be returned by a search, even before actually hitting the search button
- HTML-based chat (GChat style)
- ...
We will use the following example: when the page loads, a form is displayed, with a select list of all the tables in our database. When the user selects one of the tables, we will dynamically (without page reload) load the list of fields in that table.
Building the form
Assuming that you have a working MVC component; in the view.html.php, let's prepare our first list: the list of tables in the database. Joomla! has a built-in method for this: getTableListThe code is:
$db =& JFactory::getDBO();
$options[] = JHTML::_( 'select.option', '', 'Select a table' );
$tables = $db->getTableList();
// The function returns an array, so we cant use it straight into a JHTML::_( 'select.genericlist' , we have to create the options
foreach( $tables as $t ) {
$options[] = JHTML::_( 'select.option', $t, $t );
}
$this->tables =& JHTML::_( 'select.genericlist', $options, 'db_tables', null, 'value', 'text', '', 'db_tables' );
Next we work on the layout (assuming that the layout is "default"). In default.php, the code is:
<form action="index.php" method="post" name="adminForm">
<table width="100%" border="0" cellpadding="0" cellspacing="0">
<tr>
<td align="left" width="33%" id="tables-container">
<?php echo $this->tables; ?>
</td>
<td align="left" width="33%" id="fields-container">
</td>
<td align="left" width="33%" id="console-container">
</td>
</tr>
</table>
</form>
Ok, the layout is ready. Let's move on to preparing the client-side Ajax call.
Basic Client-side request
To make things clearer at first, let's start with a very simple request that updates the td#fields-container with the populated list of fields. We can spice things up a bit later.The JS code below needs to be added to your document. Explanations on how to insert JS into a Joomla! document can be found here .
This is the javascript that needs to be added to your document (explanations for the code can be found below):
window.addEvent("domready",function(){
$("db_tables").addEvent("change",function(){var url="index.php?option=com_mycomponent&format=raw&task=listFields&table="+this.getValue();var a=new Ajax(url,{});});method:"get",}).request();
update:$("fields-container")
The domready event is similar to onLoad except that it is triggered as soon as the DOM is ready (which means for example that you don't need to wait until images are loaded). Finally, the dollar selector: $('db_tables') is the Mootools shortcut for document.getElementById('db_tables')
So, the first two lines mean:
// As soon as the DOM is ready, do the followingNext line is preparing the request URL. The vital part is to understand that an Ajax request is just another request to your webserver. So imagine if you were to click on a link that has the exact same url, what would happen? You would be directed to the component called com_mycomponent and the task listFields would be called. It's exactly what will happen here. We'll take care of the server side in the next section.
window.addEvent('domready',function(){
// When the select list with id 'db_tables' is changed, do the following$('db_tables').addEvent('change',function(){
The getValue() method is the best (Mootools) way to get the value of a form element because it works on any type of input: value of a text input, selected option of a select list, even gives you an array if you are working on a mutliple select list.
Finally, understanding the scope in Mootools is a bit tougher and we'll skip this part here.... so to cut it short, the reference this means the $('db_tables') element.
// the url will look like e.g.: index.php?option=com_mycomponent&format=raw&task=listFields&table=jos_content ; this can be used to debug the server-side by putting this url in your browser address
var url='index.php?option=com_mycomponent&format=raw&task=listFields&table='+this.getValue();
That object Ajax takes two parameters: the url of the request which we have just built above and an object of options. The options we are setting are the method (post or get) and the DOM element that we want the request to update on success. A lot more options can be set, in particular a onComplete function to handle more than just updating a <td> (see chapter Spicing up the client side).
// Create the Ajax object with the url we just builtFinally, the Ajax class has a method called request() which actually sends the request. Mootools is able to chain functions so after creating the Ajax object (that's actually calling a function called initialize) we straight away send the request.
var a = new Ajax(url,{
// And setting some options
method:'get',
update:$('fields-container')
If you have firebug installed you will see in the console that a request has been send:

Note: the response should be an error for now since we have not yet taken care of the server side and the request would appear red in firebug.
Note2: the option would show com_mycomponent, not com_weblinks in the image above
Server-side response
The server-side response is quite straight forward if you are familiar with the Joomla! MVC. The request is directed to the controller of our component com_mycomponent. So in that controller we create a function with the same name as the task from the request; in controller.php:function listFields() {
// Get the value of the table variable, don't forget to cast its type (cmd should do here)
if( $table = JRequest::getVar( 'table', '', 'get', 'cmd' ) ) {
$db =& JFactory::getDBO();
// Grab the fields for the selected table
$fields =& $db->getTableFields( $table, true );
if( sizeof( $fields[$table] ) ) {
// We found some fields so let's create the HTML list
$options = array();
foreach( $fields[$table] as $field => $type ) {
$options[] = JHTML::_( 'select.option', $field, $field );
}
$list =& JHTML::_( 'select.genericlist', $options, 'table_fields', null, 'value', 'text', '', 'table_fields' );
// Remember that this is the same as a normal request, so displaying means echo, not return the list
echo $list;
// Return to keep the application from going anywhere else
return;
}
} else {
echo JText::_( 'No table selected' );
}
}
That's it! When you change the value selected in your form, you should see the correct list of fields appear in the second <td>
Important note: The value format=raw in the url is necessary to avoid getting the entire Joomla! template as a response.
Arguably, we should probably create a view.raw.php etc... I just think that it's not necessary in this simple case.
Spicing up the client-side
Two of the major issues when using Ajax are:- difficulties to bookmark the page if content is loaded using Ajax, especially for IE users
- the user sometimes does not realize that something has happened, because the "loading" animation in the browser has not shown anything
The second issue though, can easily be handled by adding a few things to our client side.
So, what we'll do in this section is:
- Add a "loading" effect in place of the field list while Ajax is working;
- Display a message stating that we've loaded a list; This is where we'll use JSon and the third <td> in our layout
- Use a Mootools visual effect
Replace the JS code used in the first part with this:
window.addEvent("domready",function(){
var fx=new Fx.Style($("console-container"), "background-color", {duration:2000});
$("db_tables").addEvent("change",function(){
$("fields-container").empty().addClass("ajax-loading");
var url="index.php?option=com_mycomponent&format=raw&task=listFields&table="+this.getValue();
var a=new Ajax(url,{
method:"get",
onComplete: function(response){
var resp=Json.evaluate(response);
$("fields-container").removeClass("ajax-loading").setHTML(resp.html);
$("console-container").setHTML(resp.msg);
fx.set("#fff").start("#f60").chain(function(){
this.start.delay(2000,this,"#000");
});
}
}).request();
});
});
JS Code Explanations
var fx=new Fx.Style($('console-container'), 'background-color', {duration:2000});Create an object of the class Fx.Style, which will affect the DOM element $('console-container'), working on its style: background-color and for a duration of 2 seconds. Remember that Mootools is object oriented. We have just created the instance of an effect object, we'll have to start the effect later on.
$('fields-container').empty().addClass('ajax-loading');What happens when we change the select list of tables? We empty the $('fields-container') element (clear the old list of fields) and dynamically add a CSS class. What the class looks like is up to you. But usually adding a rotating "load", of any kind, is nice. You can get those as animated gifs at Ajaxload.info . Make that gif the background of the class ajax-loading and you're all set.
Nothing else changes in the code until the onComplete method:
onComplete: function(response){Again remember that Mootools is object-oriented. Ajax is a class, it has parameters but also methods. onComplete is one that you can define/overload. It's a function and can do anything you want it to.
var resp=Json.evaluate(response);
$("fields-container").removeClass("ajax-loading").setHTML(resp.html);
$("console-container").setHTML(resp.msg);
fx.set("#fff").start("#f60").chain(function(){
this.start.delay(2000,this,"#000");
});
}
First of all, we are going to receive a JSon-encoded object as a response (see the modifications on the server side below). So we need to transform it back into a JS object with Json.evaluate.
Second, we remove the dynamically added class (loading gif) and set the HTML inside the fields-container td from the JS object resp (grab it's property html):
$('fields-container').removeClass('ajax-loading').setHTML(resp.html);Next we add the message stating that we've loaded something, to the message container td.
$('console-container').setHTML(resp.msg);Finally, we start the Mootools effect. First, set the background to white, then start the 2-second effect from current color (white) to ... orange (#f60).
Chain is another great but a bit more advanced feature of Mootools. It means that after the start function is complete (entire effect) we start another function (the one inside chain()). In that function, this represents a pointer to the fx object.
So we will use the start function of that fx object again (this.start) but with a delay of 2 seconds: this.start.delay(2000,this,'#000') .
In Mootools, a function is also an object with parameters and methods. The function object has a method called delay, to which you need to pass the time to wait, bind the fx object (this) and pass the parameter which you would normally pass to the start function ('#000').
fx.set('#fff').start('#f60').chain(function(){
this.start.delay(2000,this,'#000');
});
Ok, this last part might have been a bit of a tough one... I just hope that it can be reused and tweaked by most programmers who read this tutorial to suit their needs.
Also please disregard how ugly the effect is, I just wanted you to see clearly that the effect is running.
Server-side
Of course, we need to modify the server side a bit as well to interact properly with the spiced-up client-side.Note: this will only work with PHP5. If you have PHP4, you need to install the PEAR JSon library.
function listFields() {And now, you can test.
// Get the value of the table variable, don't forget to cast its type (cmd should do here)
if( $table = JRequest::getVar( 'table', '', 'get', 'cmd' ) ) {
$db =& JFactory::getDBO();
// Grab the fields for the selected table
$fields =& $db->getTableFields( $table, true );
if( sizeof( $fields[$table] ) ) {
// We found some fields so let's create the HTML list
$options = array();
foreach( $fields[$table] as $field => $type ) {
$options[] = JHTML::_( 'select.option', $field, $field );
}
$list =& JHTML::_( 'select.genericlist', $options, 'table_fields', null, 'value', 'text', '', 'table_fields' );
// If failure, even though we have nothing to respond as HTML, we set response['html'] to an empty string only to avoid JS errors on the client side.
if( !$list ) {
$response['html'] = '';
$response['msg'] = JText::_( 'Failed to build fields list for table' ) . ' ' . $table;
}
// HTML part of our response is the list, and there is a message as well
$response['html'] = $list;
$response['msg'] = JText::_( 'Successfully loaded fields for table' ) . ' ' . $table;
}
} else {
// If failure, even though we have nothing to respond as HTML, we set response['html'] to an empty string only to avoid JS errors on the client side.
$response['html'] = '';
$response['msg'] = JText::_( 'No table selected' );
}
// Encode and echo
echo ( json_encode( $response ) );
// Return to keep the application from going anywhere else
return;
}
Hope this tutorial will help all who want to use AJAX in their Joomla! applications. Once you are comfortable with Mootools and Ajax, some really powerful features can be created.
Comments and suggestions are always welcome.
Good luck to all!
Mat


2009-01-31 22:14:50
2009-02-04 19:15:24
2009-02-20 22:07:58
I'm development a component and your article help me so much!!
But i cant do something...
I have 3 dependent select lists and after select de first option the second list its updated, but I can't get the changes on the second list for update the third list.
Maybe you can help with this thing posting here something or reply to my mail.
Thanks!!!
PD: Sorry for my poor english but doesn't my natural language
2009-03-14 20:09:59
I'm french. I'm begin Joomla development and i see your bueatiful component.
I search how make a asynchrone request with Joomla and Mootols and a php file with requets BDD.
Can you give me a simple example please .
Thank you very much!!
See you soon.
Julien
2009-06-02 15:22:07
I have a form with 2 dependent select lists and i cant apply the change event on the the second one to be able to see the final result of the form.
Could you give me a clue on that?
thanks in advance
PS: Great article very helpful, congrats.
2009-11-15 15:52:38
Only problem is that I get the following error ...
Warning: Memcache::addserver() expects parameter 2 to be long, string given in /Library/WebServer/Documents/testsite/libraries/joomla/cache/storage/memcache.php on line 84 MODEL::__construct()
In 'view.html.php', I used $this->assignRef() to assign the the HTML table list to $this->tables in 'default.php'.