AJAX CakePHP

Using AJAX with CakePHP tutorial

CakePHP is a wonderful framework. Unfortunately, like a lot of software, documentation is severly lacking. The effort is definitely there, but with an API containing the occassional "Enter description here... unknown_type", it definitely makes things a bit more difficult. Especially for someone who adopted the RTFM mantra many years ago.

For this article, we're going to try and figure out how to get AJAX and cake to work together without having to comb cake's manual, wiki, the bakery, users' sites and tutorials and Google groups.

First things first - you'll need to have cake installed and running.

Next things second - you'll need to get the scriptaculous and prototype libraries. (I haven't tried any other AJAX libraries, but these seem to work just fine for now).

Note: there was an issue where the code crashed firefox that turned out to be caused by the prototype.js that's included in scriptaculous being the incorrect version. Therefore, I recommend downloading them separately.

You'll need to place the scriptaculous and prototype files in /app/webroot/js directory.

Now you need to add the following inside the <head> tag of your layout file (/app/views/layout/default.thtml) to make them available:

<?php 
print $html->charsetTag('UTF-8');
print $javascript->link('prototype');
print $javascript->link('scriptaculous.js?load=effects'); 
?>

At this point, you have 2 options - to use the RequestHandler component or not. If you're doing fancier AJAX things, you can check CakePHP's manual on RequestHandler, but to keep things simple, we won't use it.

In your controller's action, you want to add:

// controller file
var $helpers = array('Html', 'Javascript', 'Ajax');
 
function view() {
    $this->render('layout file', 'ajax');
}
First we want to make sure we're using cake's built-in helpers, including the AJAX helper. In your action, you'll change the render action to include the ajax option, which tells cake to crunch the information and send the data somewhere rather than displaying a new page. We'll see where in a second.

What you want to do next is create a link, but not just any link. You want to enable an ajax link, and to do that you use the ajax helper that comes with cake.

// view file
echo $ajax->link('link text', '/controller/action', 
                 array('update' => 'div id')
                );
This will automatically generate a link with the appropriate attributes to output the ajax into the div you specify. Note: don't forget the forward slash - cake needs to be able to find your controller, otherwise nothing will appear to happen (no errors will be shown either).

Styling it

If you want to add a css class to the link, add the undocumented 'class' option as part of the options array.

// view file
echo $ajax->link('link text', '/controller/action', 
                 array('update' => 'div id', 'class' => 'aclass'));

What about images?

To include an image to be linked rather than text, you simply include the html for the image in place of the first parameter, but be sure to add FALSE so the code doesn't get escaped.
echo $ajax->link('<img src="/img/button.gif" border="0"/>', 
                 '/controller/action/',
                 array('update' => 'div id'),
                 null, 
                 FALSE
                );

Multiple divs

That's all fine and dandy, but what if you wanted to update more than one div at a time? To do that, you'll need to add an array of divs to update, and also use the $ajax helper to create the divs.
echo $ajax->link('link text', 'controller/action', 
                 array('update' => array('div1', 'div2'))
                );
 
// create the div code rather than hard-coding <div id='div1'>
echo $ajax->div('div1');
echo $ajax->divEnd('div1');
 
echo $ajax->div('div2');
echo $ajax->divEnd('div2');

So it updates. How do I make it look cool?

To add effects, you'll need to add the effect to the link:
$ajax->link('link text', 'controller/action', 
            array('update' => 'div id', 
                  'loaded' => 'scriptaculous effect')
           );
You can use loaded/loading or any of the other available options listed in the manual under helpers.

A complete example

We'll create a simple example here to demonstrate.
<?php
/**
 * $Id: approot/controllers/things_controller.php 
 */
 
class ThingsController extends AppController
{
    var $name = 'Things';
    var $helpers = array('Html', 'Javascript', 'Ajax');
 
    // we're not going to use a model for this example, but
    // it would be easy to use a database thanks to cake
    var $uses = null;
 
    /**
     * initial page load 
     */
    function index() {
 
        // preload dynamic data
        $this->set('data1', 'content will update here');
        $this->set('data2', 'here too');
 
        $this->render('neat');
 
    }//index()
 
    /**
     * display content action
     *
     * @param int id of content to display
     */
    function view($id) {
 
        // content could come from a database, xml, etc.
        $content = array(
                        array('somebody is baking brownies',
                              'become a cake baker',),
                        array('knowledge is not enough',
                              'we must also apply - bruce lee')
                     );
 
        $this->set('data1', $content[$id][0]);
        $this->set('data2', $content[$id][1]);
 
        // use ajax layout
        $this->render('neat', 'ajax');
 
    }//view()
 
}//ThingsController
?>
<!-- // approot/views/layouts/default.thtml -->
<html>
<head>
<?php 
print $html->charsetTag('UTF-8');
print $javascript->link('prototype');
print $javascript->link('scriptaculous.js?load=effects');
?>
</head><body>
<?php echo $content_for_layout; ?>
</body></html>
<!-- // approot/views/things/neat.thtml -->
<h1>Really neat stuff here</h1>
<?php 
// update both divs 
echo $ajax->link('update divs', '/things/view/0', 
                 array('update' => array('dynamic1', 'dynamic2'))
                );
echo ' | ';
// use an effect
echo $ajax->link('blinders', '/things/view/1', 
                 array('update' => array('dynamic1', 'dynamic2'),
                       'loading' => 'Effect.BlindDown(\'dynamic1\')')
                );
?>
 
<hr/>
<?php echo $ajax->div('dynamic1'); ?>
<h2><span><?php echo $data1; ?></span></h2>
<?php echo $ajax->divEnd('dynamic1'); ?>
 
<?php echo $ajax->div('dynamic2'); ?>
<h3><span><?php echo $data2; ?></span></h3>
<?php echo $ajax->divEnd('dynamic2'); ?>

Download

ajax_example.zip

References

BACK TO TOP