Thursday, June 24, 2010

Zend Form: display group and custom decorator

I keep meaning to write a proper review of Zend Framework, but I am waiting to finish a big project that uses it. It is running late, and I think ZF can take some of the blame. So don't hold your breath waiting for a positive review.

Today's topic: I want to have part of my form hidden initially, and instead show a button saying: "toggle more questions".

So, we make it a display group (where ex1 and ex2 are the form elements to hide):

By default this wraps it in a fieldset, with no place I could see to slip in my CSS, javascript and the toggle link. What I need is a decorator, I said to myself.

Oh, gasp, does Zend make this difficult or what! In another part of this project custom decorators had been used for a minor layout change. 7 classes in 3 directories, almost all of it boilerplate. The real work was being done in CSS; those 7 classes were just to give a way to name the items as far as I could tell.

The problem is the Zend Framework Philosophy of making things complex. Did you realize there is no addDecorator($myclass) function! You have to keep decorators in a special directory and then tell Zend where to get them. Then addDecorator('part_of_my_class_name').

ZF's saving grace is that it is open source. So I poked around, and here is my solution. First, I'll define an alias for readability (optional):

Remove all the default stuff I don't need (this step is optional too):

Now insert my decorator (these 4 lines are in lieu of addDecorator($myclass)):
  $decorators['MyTest']=new MyTestDecorator();

(I.e. get the current decorators, add mine, then replace the existing decorators with the new set.)

Finally we get to the meat. All you need is to define a render() function that takes a string (the existing content) and returns that string, optionally modified. Here is the minimal version that does nothing.
class MyTestDecorator extends Zend_Form_Decorator_Abstract
  public function render($content){ return $content; }

And here is the full version: it hides the elements in the group and uses JQuery to show/hide it. The CSS is inline.
class MyTestDecorator extends Zend_Form_Decorator_Abstract
  public function render($content)
    $js="$('#extra_questions').toggle();return false;";
    return '<a href="" onclick="'.$js.'">Toggle Tags Visibility</a>'.
      '<div id="extra_questions" style="display: none;">'.$content.'</div>';

Yep, it's that simple. Oooh, the architects of ZF must be turning red with rage ;-)

UPDATE: The best article I've found so far on Zend Form Decorators. As many of the comments say, the length of the article is also a very good argument against using Zend Form. And it still didn't answer my questions, so it really needed to be 2-3 times as long. But if you need to format a form, it is a far more useful resource than the utterly inadequate Zend Form manual.


Anonymous said...

Thanks that was very useful. I'm currently using Zend for a rather big project and I had no idea of the amount of unhappiness it was going to cause me with things like this!

darren said...

A helpful article on using DisplayGroups: