Zend Framework context switching for HTML content

I tried to stay away from using javascript as much as I could, but even I could not escape from developing AJAX feature rich applications. I have chosen jQuery as my poison.

But I had already build my apps using static HTML output generated by Zend Framework, so how could I add this richness to my apps without refactoring most of my code ? Simple, by using Zend_Controller_Action_Helper_ContextSwitch, ZendX_JQuery and some minor adjustments to my view scripts.

If you have downloaded the latest Zend Framework package, you'll notice in the extras/library directory the extra jQuery component, so make sure you also include that library path to your application's include path.

The following code was tested with Zend Framework version 1.10.3.

In your controller, you have actions that displays a form and one that processes it. So, to have it processed without refreshing the page in the browser, put the following lines in your controller:

public function init()
{
    $this->_helper->contextSwitch()
         ->setContext(
             'html', array(
                 'suffix'    => 'html',
                 'headers'   => array(
                     'Content-Type' => 'text/html; Charset=UTF-8',
                 ),
             )
         )
         ->addActionContext('index', array('html','xml', 'json'))
         ->setAutoJsonSerialization(true)
         ->initContext();
}

This context switch routine allows me to use my data in HTML, XML and JSON format. Both XML and JSON are default output formats supported by Zend Framework, but the documentation didn't contain a proper example on how to output HTML.

Consider you already have put a lot of time in rendering a table according to the specific form parameters, you don't want to re-do this work to display it again to use XML or JSON formatted data. Easiest (not best) way is to have that data returned in your view without the rest of the layout spoiling your AJAX requests.

Using the context switcher defined above, we only need to add a view template using the "actionname.html.phtml" naming convention. So for indexAction it would have index.phtml for normal displaying, index.xml.phtml for XML generated output, index.json.phtml for json-formated data and index.html.phtml to have your content returned without layout.

In our requesting view template we now only have to add a container where to put that data. Since I return the full generated table, I wrap my "table" tag in a "div" block.

<div id="tableContainer">
<table>...</table>
</div>

In jQuery I use the following function to load data inside that container:

function loadData(link, placeholder)
{
    if (-1 === link.indexOf('/format/html') && -1 === link.indexOf('format=html')) {
        link += '/format/html';
    }
    jQuery.get(link, function(data) {
        var container = '#' + placeholder;
        jQuery(container).html(data);
    });
    return false;
}


A common link like
<a href="/path/to/next/page">next page</a>
becomes
<a onClick="loadData(this.href, 'tableContainer');" href="/path/to/next/page">next page</a>

And with ZendX_JQuery you don't even need to load all the jQuery scripts, Zend Framework does this for you.

First modify your bootstrapper so your view uses the jQuery components:

protected function _initView()
{
    // Initialize view
    $view = new Zend_View();
       
    // Adding jQuery view helpers
    $view->addHelperPath("ZendX/JQuery/View/Helper", "ZendX_JQuery_View_Helper");

    // Add it to the ViewRenderer
    $viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper(
        'ViewRenderer'
    );
       
    $view->doctype('XHTML1_STRICT');
    $view->headMeta()->appendHttpEquiv('Content-Type', 'text/html; Charset=UTF-8');



    // Add a stylesheet for JQuery parts, only when it's required
    $view->jQuery()->addStyleSheet(

        $view->baseUrl('/css/ui-lightness/jquery-ui-1.7.2.custom.css')
    );
 

    $viewRenderer->setView($view);
    Zend_Controller_Action_HelperBroker::addHelper($viewRenderer);

    // Return it, so that it can be stored by the bootstrap
    return $view;
}


In your layout template, you can add between the head tags the following call:

<head>
...
    <?php echo $this->JQuery() ?>
...
</head>

That's all there is to it. Now you can add nice date pickers and other awesome jQuery and jQuery UI elments to your apps.

I know it's not the best way to AJAX-ify your apps, but it's the easiest way to convert a non-javascripted app into a more user friendly application. A nice side-effect is your app remains working for non-javascript browsers and web spiders (like those for search engines).

If you want to learn more about ZendX_JQuery, be sure to check out a presentation given by Dennis De Cock at one of the PHPBenelux UG meetings. His session has inspired me to take a deeper look at jQuery as a javascript library and ZendX_JQuery as the tool to master it.

Comments

  1. So if you want cleanly-separated event bindings, don't use Zend Framework? ;-)

    ReplyDelete
  2. Thanks DragonBe for your article, I didn't know ZendX_JQuery yet and it seems to be a nice solution here. I also like your loadData() function, short and easy.

    ReplyDelete
  3. Pretty useful article - thanks! I'm working on something similar these days and I think this will help me a lot :)

    BTW jQuery offers an even shorter API to AJAX-load HTML content into a container you could do something like:

    jQuery('#' + placeholder).load(link);

    Instead of most of the code in loadData()
    - see http://api.jquery.com/load/

    ReplyDelete
  4. @PHPGangsta,


    Glad to have inspired you. Once you've build stuff yourself, blog about it and share the knowledge.

    @Shahar,

    Thanks for the jQuery load() hint, I didn't know about it but I'm sure going to use it from now on.

    ReplyDelete
  5. This comment has been removed by a blog administrator.

    ReplyDelete
  6. @Hire PHP Programmer,


    You're more then welcome to publish your services on my blog, but then we need to negotiate advertisement fees and stuff like that, for which I don't have the time for.

    If you have a true commitment to the community, post something valuable like code samples and add a link to your company at the bottom. But stay on topic.

    ReplyDelete
  7. There seems to be a simpler method

    in init() of controller put

    $AjaxContext = $this->_helper->getHelper('AjaxContext');
    $AjaxContext->addActionContext('index','html')
    )
    ->initContext();

    Then write ajax request handling code in index.ajax.phtml file

    ReplyDelete
  8. Do this code runs under all PHP 5 modules.

    ReplyDelete

Post a Comment

Popular Posts