Using dijit.Editor with Zend Framework

We're getting ready to release Zend Framework 1.6.0. However, one important Dijit had to be omitted from the release as I was not able to get it working in time: dijit.Editor.

This dijit is important as it provides an out-of-the-box WYSIWYG editor that you can use with your forms. Unfortunately, actually using it with forms is pretty tricky — Dojo actually ends up storing content outside the form, which means you need to create a handler that pulls the content into a hidden element when saving.

I have created an implementation, however, that you can start using now, and I'm posting it below. It includes both a view helper for displaying it, as well as a form element for use with Zend_Form.

The View Helper looks like this:

<?php
/** Zend_Dojo_View_Helper_Textarea */
require_once 'Zend/Dojo/View/Helper/Textarea.php';

/**
 * dijit.Editor view helper
 * 
 * @uses       Zend_Dojo_View_Helper_Textarea
 * @category   My
 * @package    My_View
 * @subpackage Helper
 * @license    New BSD {@link http://framework.zend.com/license/new-bsd}
 * @version    $Id: $
 */
class My_View_Helper_Editor extends Zend_Dojo_View_Helper_Textarea
{
    /**
     * @param string Dijit type
     */
    protected $_dijit = 'dijit.Editor';

    /**
     * @param string Dojo module
     */
    protected $_module = 'dijit.Editor';

    /**
     * dijit.Editor
     * 
     * @param  string $id 
     * @param  string $value 
     * @param  array $params 
     * @param  array $attribs 
     * @return string
     */
    public function editor($id, $value = null, $params = array(), $attribs = array())
    {
        $hiddenName = $textareaName = $id;

        $hiddenAttribs = array(
            'id'    => $hiddenName,
            'name'  => $hiddenName,
            'value' => $value,
            'type'  => 'hidden',
        );

        if (array_key_exists('id', $attribs)) {
            $hiddenAttribs['id'] = $attribs['id'];
            $attribs['id'] .= 'Editor';
            $id = $attribs['id'];
        }

        if (']' == $textareaName[strlen($textareaName) - 1]) {
            $textareaName = rtrim($textareaName, ']');
            $textareaName .= 'Editor]';
        }

        $this->_createGetParentFormFunction();
        $this->_createEditorOnSubmit($hiddenAttribs['id'], $id);

        $html = '<input' . $this->_htmlAttribs($hiddenAttribs) . $this->getClosingBracket()
              . $this->textarea($textareaName, $value, $params, $attribs);
        return $html;
    }

    /**
     * Create JS function for retrieving parent form
     * 
     * @return void
     */
    protected function _createGetParentFormFunction()
    {
        $function =<<<EOJ
if (zend == undefined) {
    var zend = {};
}
zend.findParentForm = function(elementNode) {
    while (elementNode.nodeName.toLowerCase() != 'form') {
        elementNode = elementNode.parentNode;
    }
    return elementNode;
};
EOJ;

        $this->dojo->addJavascript($function);
    }

    /**
     * Create onSubmit binding for element
     * 
     * @param  string $hiddenId 
     * @param  string $editorId 
     * @return void
     */
    protected function _createEditorOnSubmit($hiddenId, $editorId)
    {
        $this->dojo->onLoadCaptureStart();
        echo <<<EOJ
function() {
    var form = zend.findParentForm(dojo.byId('$hiddenId'));
    dojo.connect(form, 'onsubmit', function () {
        dojo.byId('$hiddenId').value = dijit.byId('$editorId').getValue(false);
    });
}
EOJ;
        $this->dojo->onLoadCaptureEnd();
    }
}

There's a lot of code in there, but the important bits are the last two methods, which allow finding the parent form of the Editor dijit, and then tying the form onsubmit event to an action that sets a hidden value to the provided Editor content.

The form element is much easier:

<?php
/** Zend_Dojo_Form_Element_Dijit */
require_once 'Zend/Dojo/Form/Element/Dijit.php';

/**
 * dijit.Editor
 * 
 * @uses       Zend_Dojo_Form_Element_Dijit
 * @category   My
 * @package    My_Form
 * @subpackage Element
 * @license    New BSD {@link http://framework.zend.com/license/new-bsd}
 * @version    $Id: $
 */
class My_Form_Element_Editor extends Zend_Dojo_Form_Element_Dijit
{
    /**
     * @var string View helper
     */
    public $helper = 'Editor';
}

Honestly, that's it. Since the view helper does the heavy lifting of display, all the element needs to do is to indicate which helper to use.

Putting it all together in a form, you'll need to do the following:

$view->addHelperPath('My/View/Helper/', 'My_View_Helper');
$form->addPrefixPath('My_Form_Element', 'My/Form/Element');

$form->addElement('Editor', 'content');

The helper prefix path is probably best added in your bootstrap; the form prefix path should be added early in your form class's init() method, or passed in via configuration.

You should see this element shipped in ZF's standard library likely in ZF 1.6.1.

Update: Jasone E. noted that the view helper was missing a $_module declaration of dijit.Editor — this has been added.