AJAX Helpers in Cotonti

Things which make Web 2.0 closer to you

In this article we are not going to explain what AJAX is, how it works and the basics of AJAX programming. This article explains how new features introduced in Cotonti Siena help with basic use of AJAX.
 

#1. Server-side Help

You might probably know that Cotonti has support for AJAX parts in plugins. There is an article explaining it, so you should start with it if you haven't read it already. Since 0.9.0 AJAX support is not restricted only to plugins. You can develop AJAX-enabled modules and find yourself working in AJAX mode easily.

If the page was requested via asynchronous call, Cotonti detects it and sets COT_AJAX constant TRUE. In normal request mode it's FALSE. This constant is available from everywhere, so you can use it to define specific behavior:

if (COT_AJAX)
{
	// AJAX-specific code here
}
else
{
	// Normal mode code here
}

// Common code here

 

What's more, Cotonti keeps a watchful eye whether the request is asynchronous or not too. In AJAX mode it does not output header and footer of the page, although header.php and footer.php are still included for consistency.

You probably expect more functions, classes and tools, but PHP provides quite handy toolkit for handling XML, JSON and other technologies used in AJAX, so we don't have any Cotonti-specific solutions for them.

If you think that something is missing, make a feature request.
 

 

#2. Automatic AJAX Enablement

This feature is available if both jQuery and AJAX are enabled in Administration => Configuration => Main Settings.
 

#2.1. The simple way

Some AJAX functionality is already available to you out of the box. Well, not AJAX but AJAH to be correct. As we mentioned, header and footer are not rendered for asynchronous requests. So you can load contents of any page to a specific div. The default id for such div is ajaxBlock. So, let's assume you have put such contents in your template:

<div id="ajaxBlock">
	<!-- here goes the normal contents of the page -->
</div>

 

Then you can make a hyperlink that will open via AJAX in that div by applying class="ajax" on it. For example, it may be a page hyperlink that will load in ajaxBlock:

 

<a href="page.php?al=my_page" class="ajax">Load my page in div</a>

 

Cotonti will query a page from href via AJAX and display the results in ajaxBlock.

Now you've got asynchronous links, but how about form submission? It is as easy! Just add class="ajax" to the form tag and don't forget that a form must have a unique id attribute for ajax to grab its contents. Example:

 

<form id="my_form" action="{MY_FORM_ACTION}" method="post" class="ajax">
	<input type="text" class="text" name="title" value="{MY_FORM_TITLE}"  />
	<input type="submit" class="submit" value="{PHP.L.Submit}" />
</form>

 

On submit data from this form will be grabbed and sent via ajax. The result will be displayed in ajaxBlock.

Of course you can show results in divs other than ajaxBlock and have multiple ajaxable divs on your page. To specify a target div for an ajax form, put the div name in the class attribute of the form, using the hash address syntax explained in the next chapter. We had to use this attribute because it is the only way to pass XHTML validation. Example:

 

<div id="myDivId">
	<form id="my_form" action="{MY_FORM_ACTION}" method="post" class="ajax post-myDivId">
		<input type="text" class="text" name="title" value="{MY_FORM_TITLE}"  />
		<input type="submit" class="submit" value="{PHP.L.Submit}" />
	</form>
</div>

 

This form will be submitted and shown in myDivId div.

History and bookmarking is not available for POST forms for security reason. It would be quite strange to bookmark such temporary links too.

To open a hyperlink in a custom div, a more complex syntax is used, because it is unified with hash addresses explained in next chapter. Here is an example of page link that opens in a custom div:

 

<a href="page.php?al=my_page"
  class="ajax"
  rel="get-myDivId">
    Load my page in div
</a>

 

It will still take the URL from href attribute, and the target div id is specifed after "get-" in rel attribute. Even more complicated actions are available via get-rel, but first you need to understand ajax hash addresses.
 

 

#2.2. Understanding and applying hash addresses

If you tried clicking a link from the previous example, most probably you would see something similar to this in your browser address bar:

http://localhost/cotonti/some_script.php?m=some_params#get-myDivId;page.php;al=my_page

 

You see the ajax hash address after the hash (#) sign. Hash addresses are used in AJAX-enabled applications for 2 obvious reasons:

 

  • To enable bookmarking of AJAXed contents
  • To support browser history buttons in AJAX-enabled applications

 

In Cotonti the format of a hash address is:

get|post[-DIV_ID][;PATH_TO_SCRIPT][;QUERY_STRING]

Here square braces mean that the part is optional and can be omitted. The parts are separated with semicolons (;). The hash address begins with get keyword which means that the page is going to be retrieved with a GET request, or it may begin with post keyword only if you write it in title attribute of a POST form (so the form is to be sent via POST).

 

  • DIV_ID – stands for target div id, which will be used for result output. See examples in previous chapter.
  • PATH_TO_SCRIPT – is a server-relative or relative path to the target script. Usually it is just something like "page.php" or "admin.php" (without the quotes). Url-transformed paths are also supported, so /categories/books/prose is also a valid script path (if it points for example to list.php via mod_rewrite). Finally, this part can be omitted if exactly the same script as current one is to be used (e.g. if the link is on users.php, you can omit users.php part and continue with query string).
  • QUERY_STRING – is what you usually see in URLs after question mark (?). It is a list of keys=values separated wih & (in address bar) or &amp; (in XHTML attributes). We didn't use question mark as separator because some browsers don't allow multiple question marks in address bar.

 

Here are a few examples of hash addresses:
 

 

get;m=config&s=main

This will query currents script with parameters m=config&s=main and display results in ajaxBlock.
 

get-container;m=config&s=main

The same as previous example, but div with id="container" will be used for output.
 

get;list.php;c=articles

This will load list.php?c=articles to ajaxBlock.
 

get-mainPreview;index.php;

This will load index.php with no parameters into div with id="mainPreview".
 

get-posts;plug.php;e=posts&id=123

This will use "posts" div to output results of asynchronous plug.php?e=posts&id=123 call.
 

post-form_container;plug.php;e=submitter

This hash address can be used in POST forms only, it will submit form data to plug.php?e=submitter via AJAX.

Hash addresses can be used in address bar after hash mark (#) as mentioned above. For example, you can have some fun loading entire page into the right column of index page by entering an URL like

<a href="http://localhost/cotonti/index.php#get-right;page.php;al=ajax_helpers">ajax helpers doc</a>

 

They also can be used in rel attribute of hyperlinks. It is very important if the link to AJAX version of the target is different from normal version. For example, if a plugin hooks into user's profile and displays some data there, you have an AJAX part of the plugin which updates the plugin's area only. But search engines and noscript users will have to update entire user profile. So the links are different. Here is how it can be solved:

 

<a href="users.php?m=details&amp;id=3&amp;u=John" 
  class="ajax"
  rel="get-points;plug.php;e=points&uid=3">
    Update points view
</a>

 

The URLs can be constructed with cot_url() function. But '?' should still be replaced with ';' afterwards for browser compatibility.

Note that AJAX hash addresses can be used in rel attribute of hyperlinks as well as other rel keywords. Just use space as separator. Here is an example with popular nofollow keyword:

 

<a href="users.php?m=details&amp;id=3&amp;u=John"
  class="ajax"
  rel="nofollow get-points;plug.php;e=points&uid=3">
    Update points view
</a>

 

The benefits of hash addresses can be also applied on forms as well as on hyperlinks. In this case you should use class attribute of a form tag and put a hash address in it. Example:

 

<form id="my_form"
  action="{MY_FORM_ACTION}"
  method="post"
  class="ajax post-myDivId;{MY_FORM_AJAX_ACTION}">
	<input type="text" class="text" name="title" value="{MY_FORM_TITLE}"  />
	<input type="submit" class="submit" value="{PHP.L.Submit}" />
</form>

The format of hash address in forms is the same as for hyperlinks. The only difference is that you should use post- for POST forms and these POST forms do not affect the browser history or address bar. GET forms work the same way as hyperlinks.
 

#2.3. Enabling AJAXable pagination

Building AJAX-enabled pagination would be a tiresome job if cot_pagenav() didn't provide built-in support for AJAX helpers.

First let's consider the signature and PHPDoc of cot_pagenav() function:

/**
 * Page navigation (pagination) builder. Uses URL transformation and resource strings,
 * returns an associative array, containing:
 * ['prev'] - first and previous page buttons
 * ['main'] - buttons with page numbers, including current
 * ['next'] - next and last page buttons
 * ['last'] - last page with number
 *
 * @param string $module Site area or script name
 * @param mixed $params URL parameters as array or parameter string
 * @param int $current Current page number
 * @param int $entries Total rows
 * @param int $perpage Rows per page
 * @param string $characters It is symbol for parametre which transfer pagination
 * @param string $hash Hash part of the url (including #)
 * @param bool $ajax Add AJAX support
 * @param string $target_div Target div ID if $ajax is true
 * @param string $ajax_module Site area name for ajax if different from $module
 * @param string $ajax_params URL parameters for ajax if $ajax_module is not empty
 * @return array
 */
function cot_pagenav($module, $params, $current, $entries, $perpage, $characters = 'd', $hash = '', $ajax = false, $target_div = '', $ajax_module = '', $ajax_params = array());

 

Basically you build pagination with it this way:

 

$pagenav = cot_pagenav('plug', 'e=myplug&item=' . $item_id, $d, $totalitems, $perpage);

In case of automatic AJAX enablement and use of the same URLs for AJAX and ajaxBlock for output, you just need to set $ajax parameter TRUE:

$pagenav = cot_pagenav('plug', 'e=myplug&item=' . $item_id, $d, $totalitems, $perpage, 'd', '', true);

Then if you need to specify target div ID, use 8th parameter as well:

$pagenav = cot_pagenav('plug', 'e=myplug&item=' . $item_id, $d, $totalitems, $perpage, 'd', '', true, 'myDivId');

 

The 9th and 10th parameters are used in case the AJAX urls are different from normal hrefs. Here is an example pagination for our "user points" in users.details:

 

$pagenav = cot_pagenav('users', 'm=details&id=' . $urr['user_id'] . '&u=' . $urr['user_name'], $dpt, $pt_items, $pt_perpage, 'dpt', '', true, 'points', 'plug', 'e=points&uid=' . $urr['user_id']);

 

#3. Custom Client-side Programming

 

The above AJAX helpers are easy to use and save a lot of time in typical situations. But in case of advanced AJAX programming, you'll probably want to implement custom AJAX logic, such as using concrete data requests and XML/JSON data. That's why you are not restricted to the mechanisms described above.

First of all, plain JavaScript programming is all to your service. But what's more important, you can use jQuery with it's incredible AJAX features.

But there are still a few helper functions that you may find useful even in custom JavaScript code.

First of all, there is ajaxEnabled variable which indicates whether AJAX is enabled in Administration => Configuration => Main Settings or not. So you can check it:

if (ajaxEnabled) {
	// Do something ajaxing
}

 

There is a general purpose function called ajaxSend(). It takes a settings structure as a parameter:

 

var mySettings = {
	method: 'GET', // GET or POST, default is GET
	url: 'page.php?id=123', // Target URL. If missing, form action will be used (if present)
	data: 'foo=bar&boo=far', // POST data, optional
	formId: 'myformname', // Form ID, for form submission only
	divId: 'myDivId' // div ID for results output
};

 

A simple GET request can be done like this:

 

ajaxSend({url: 'page.php?id=123', divId: 'pagePreview'});

 

To submit a form, you'd use something like this:

 

ajaxSend({formId: 'myform', divId: 'mycontainer'});

 

ajaxSend() always returns false as a result (so it can be used as onclick event handler).

By default this function pops an alert if an AJAX request fails. You can bind custom error handler functions by using global ajaxErrorHandlers array. An error handler is a JavaScript function which takes one parameter – error message string. Example:

 

function myErrorHandler(msg) {
	$('div.error').text(msg);
}

 

Error handlers are binded to target divs. So if you want to catch errors when sending AJAX requests targeting a div with id='pagePreview', you can bind the above error handler to it this way:

 

ajaxErrorHandlers.push({divId: 'pagePreview', func: myErrorHandler});

 

You can bind multiple error handlers to the same div, they will be called sequentially.

Be default, if an AJAX error occurs, ajaxError variable is set TRUE to indicate that something went wrong. If you don't intercept it with your custom AJAX error handler, then an error message will be displayed and the normal synchronous request will be made.

To apply some hash address on current browser window, you should make a call to jQuery.history plugin, providing a hash address as its parameter:

 

$.historyLoad('get-pagePreview;page;al=mypage');

 

There is a helper function which attempts to construct hash address either from a hyperlink href or rel attribute (if present). It is called ajaxMakeHash() and you can call it like this:

 

var myHash = ajaxMakeHash($('#myLink').attr('href'), $('#myLink').attr('rel'));
$.historyLoad(myHash);

 

Actually, ajaxMakeHash() function may take up to 3 parameters:

 

  1. href - Link href or form action attribute
  2. rel - An attribute value possibly containing a hash address
  3. formData - Is passed for forms only, is 'post' for POST forms or serialized form data for GET forms

 

For example, if you want to submit a GET form via AJAX and allow user to see/modify form parameters in the address bar, you can do it like this:

 

$.historyLoad(
    ajaxMakeHash(
        $('#myForm').attr('action'), ''),
        $('#myForm').attr('class'),
        $('#myForm').serialize()
));

 

This is all for now. You may ask questions on forums or request more AJAX-related features.

 



1. ez  2010-01-30 03:04
Nice Info...
I will probably use this in a plugin I am planning to make.
thnx
EZ
2. Trustmaster  2010-01-30 03:31
Note: this article mostly describes features introduced in Cotonti v0.7.0 and later.
3. esclkm  2010-02-06 04:54
низкий поклон великим.
делаю для ПМ. реально круто. есть конечно опредленные но))
4. Trustmaster  2010-02-22 00:47
UPDATE: added support for hash-addresses in forms
5. Uch  2010-12-01 16:47
sounds great!
Only registered users can post new comments