Tuesday, 21 September 2010

Zend Framework, Dojo and MVC layout

Recently I had a real nasty problem which had me scratching my head for several days, I have recently started using the Zend Framework and Dojo framework together to allow me to create forms which have rich controls in them, such as calendar controls etc.

Initially all worked well, setting up a simple ZF application, Using Zend_Dojo to "Dojo" enable my forms and views all went without a hitch. For each control i instantiated in my forms the Zend_Dojo_View_Container attached to the view would automatically create all the required script and library calls needed to make sure the right components where integrated with the page. Everything was fantastic. Then I tried to apply the same approach to my actual application.

The difference between my test rig and my Application is that in most of the views in my application we deliberately disable the MVC layout, and render the pages using the Zend_Controller_Action::renderScript() method. The reason for this is that our view scripts are pre-assembled with all components preprocessed into single files per page, including all the layout elements.

Typically we use the following in the Controller baseclass preDispatch method


$layout = Zend_Layout::getMvcInstance();
if($layout) {
$layout->disableLayout();
}
$this->_helper->viewRenderer->setNoRender();


Generally my Controller actions look like this.


public function registerAction() {
/*
* Register for new account
*/
$register = new Form_User_Registration();
if (Zend_Auth::getInstance()->hasIdentity()) {
$this->_redirect('/');
} else if ($this->getRequest()->isPost()) {
// do the registration action here
// redirect when done
}

$this->view->register = $register;

echo $this->renderScript('pages/user/register.phtml');
}


However doing things this way, I discovered that none of the form dijit specifications had been injected into the Zend_Dojo_View_Container, so none of my controls rendered as Dojo rich controls.

The reason why this was happening became clear after a lot of debugging. In the conventional MVC mode, the "contents" of the page are rendered before the layout scripts are run. The form is rendered into the page contents and then the layout script is executed to create the page around the contents. However in my case the contents where executing in the same script as the layout, so the Dojo container was being output at the top of the script before the contents where rendered, hence the missing specifications.

The solution was simple, but non-obvious. change the assignment of the form to the view from


$this->view->register = $register;


to:


$this->view->register = (string)$register;


Which forced the __ToString() method of the Zend_Dojo_Form class to be called before the Controller renderScript() function executed, causing the dijit specs to be registered first.

This fixed the problem, and finally all my forms where being rendered correctly with Dojo Rich controls..

No comments: