One thing I really hate about forms in Zend Framework is the fact that the hidden fields are displayed as blank lines – sounds stupid but this is what really happens – and if you have more than one – let’s say 4 or 5 – their presence can become really annoying and ruin the layout. This happens because although the hidden fields aren’t shown by the browser, their wrapping tags are. ZF wraps all the form elements in additional markup with the help of decorators. The default XHTML for a hidden field in a Zend_Form looks something like this:
<dt> </dt> <dd><input type="hidden" id="myElement" name="myElement" /></dd>
The definition tags have display:block, so they insert a lot of empty spaces in the layout. Which in the end will make the designer very unhappy. The solution is to remove all the definition tags or to hide them. Since this is presentational matter, it should fall to a view helper. The first idea was to remove the tags completely for hidden fields. Like such:
class Motanelu_View_Helper_FormFixer { public function formFixer( Zend_Form $_form ) { foreach( $_form->getElements() as $element ) { if( $element instanceof Zend_Form_Element_Hidden ) { $element->removeDecorator( 'HtmlTag' ); $element->removeDecorator( 'Label' ); } } return $_form; } }
…and in the view (assuming that the helper is the the right path and will be picked out automatically by Zend Framework)…
echo $this->formFixer( $this->form );
Should do the trick. And it does. A lame trick
If there are more elements in the form, the resulting markup will look something like this:
<form enctype="application/x-www-form-urlencoded" action="" method="post"> <dl class="zend_form"> <dt><label for="name" class="optional">Enter your name</label></dt> <dd><input type="text" name="name" id="name" value="" /></dd> <input type="hidden" name="id" value="" id="id" /> <dt> </dt> <dd><input type="submit" name="submit" id="submit" value="Go go go!" /></dd> </dl> </form>
Yep, that’s right! Invalid code. Not good. Another approach would be to hide the dt and dd tags that wrap around hidden fields with CSS. First attempt:
class Motanelu_View_Helper_FormFixer { public function formFixer( Zend_Form $_form ) { foreach( $_form->getElements() as $element ) { if( $element instanceof Zend_Form_Element_Hidden ) { foreach( $element->getDecorators() as $decorator ) { $decorator->setOption( 'class', 'hidden' ); } } } return $_form; } }
But this doesn’t work as expected. The code above will produce:
<dt> </dt> <dd class="hidden"><input type="hidden" name="id" value="" id="id" /></dd>
The dt doesn’t have the hidden class added to it. After some debugging, I’ve found why. It’s not a bug, it’s an…well…undocumented feature of Zend Framework.
// Zend_Form_Decorator_Label - line 306 if (!empty($label)) { $options['class'] = $class; $label = $view->formLabel($element->getFullyQualifiedName(), trim($label), $options); } else { $label = ' '; }
I’m very curious who is the rocket scientist that came up with this idea and more important why. Why shouldn’t I be allowed to change the class of an empty label? Well, never mind…But since it’s a very bad practice to change a framework’s source code, I’ve looked for a workaround. And found one: if the element doesn’t have a label, I’ll add a blank one. Like such:
class Motanelu_View_Helper_FormFixer { public function formFixer( Zend_Form $_form ) { foreach( $_form->getElements() as $element ) { if( $element instanceof Zend_Form_Element_Hidden ) { $label = $element->getLabel(); if( empty( $label ) ) { $element->setLabel( ' ' ); } foreach( $element->getDecorators() as $decorator ) { $decorator->setOption( 'class', 'hidden' ); } } } return $_form; } }
After adding the following code to my CSS file
.hidden { display: none; }
…I can finally say that it works, that all the hidden fields stay hidden and that the resulting markup will validate even against the draconian XHTML Strict specification of the W3C.
How long till you will be a ZCE on Zend Framework?
Well, 3-4 months max…
Why not change the display to inline for all DDs? Some XHTML rule?
Because it would generate a very long possibly unbroken line of labels and form items. Just imagine how a form where all the elements have display:inline would look like…
Yummy! realmendontneednospacebars
Yeap! Real men don’t use backups either…
True men don’t kill coyotes!
Great quality post! Thanks!
Very interesting article.
Is there a way to set “hidden” class to dt element instead of setting label class “hidden”. I mean that DT is still visible and creates unnecessary space in the design.
See the code that is generated with yours helper:
Add
to your CSS file…
One alternative is this:
$hidden_elt->removeDecorator('Label');
$hidden_elt->removeDecorator('HtmlTag');
Note that you can’t stack those two, because they return a boolean, not the object itself.
Jack Tanner> if you do that, your html won’t validate. It will look something like this:
I care about standards
Tudor: yes, you’re right, that would produce invalid HTML. But it’s also possible to remove the dl/dt/dd nonsense from the form and non-hidden form elements, leaving clean, simple, and valid HTML.
Yes, this hard to remove extra markup in the wrong places and inability to place good markup where you want when you want is making me batty.
The rest works fine but the decorators seem kludgy to me.
And I’d love if the docs that showed examples made sure to include the config file w/ each one. By sticking this stuff in a config file, you can reuse validators/filters in your javascript.
Try changing the default element decorators. There is a good post about this on the dev zone, http://devzone.zend.com/article/3450.
$this->addElement('hidden', 'id', array(
'decorators' => array(
'ViewHelper',
)
));
Colin> it will generate invalid HTML code…
By the way, I recently came across this bug in ZF where it generates invalid form labels (in its default config).
http://framework.zend.com/issues/browse/ZF-6426
Zend escapes the label, so it outputs “ ” in the actual HTML. Rather than toying with the escape function, I just used the following workaround:
$element->setLabel( ‘-’ );
$decorators['Zend_Form_Decorator_Label']
->setOption( ‘disable’, ‘disable’ )
;
(Assuming that $decorators is the result of $element->getDecorators()).
(Apparently it replaced & n b s p ; with space in my above reply.)
This is much easier to really hide the hidden field: Simply delete the Decorators of the element:
$hiddenElement->setDecorators(array());
That will generate invalid xhtml…see few comments above! I want my pages to validate…
Fixing this problem with a View Helper is a waste of time imho.
I created a mother class (that extends Zend_From) for all my forms, and rewritten render() function:
So no different code is written in forms or views.
You can use even Tudor solution with my implementation.
P.S.: Tudor, make possible and easily visibile for all how to
comment code plz.Slam> You just add <pre lang=”php”>code</pre>. I’ve edited you comment and added the proper highlighting. The whole idea is keeping the outputted html clean and standards compliant.
And, as always, there are more than one ways to get things done and people adhere to different philosophies. I’ve used the view helper approach because this is a presentation issue (how the form will be displayed) and presentation logic should stay at the “view level” of the MVC paradigm.
Forms are not properly in one letter of the MVC acronym, forms are forms, in fact in a form you manage the Model/Control of a form (validators, ‘required’ etc) and also the View (decorators, label, etc), so i think that is right and legitimate to manipulate all form properties directly in form classes.
Moreover, that implementation skimp lines of code and new classes creation with the same results and no different business logic nor logic error.
Btw, this is my interpretation.
The way that the form’s widgets are rendered – with an extra CSS class in my case – is a presentation issue, that’s why I went for the view helper approach.
But, of course, one can take the best of both worlds and do it like:
This will yield valid XHTML code and reside in the parent form. It’s not a matter of right or wrong, but rather of personal opinions on design patterns.
@Tudor Your last solution will still generate dt’s without a class. At best it will add a class to the inside the .
Could you confirm this, or did you prepare your elements in a special way (changing the decorators etc).
@Tudor with “last solution” I meant your comment above http://blog.motane.lu/2009/02/17/zend-framework-and-hidden-fields/comment-page-1/#comment-1053
You should use ViewHelper and HtmlTag decorators in order to achieve this. The following piece of code will completely ignore the label tag (incl. the dl) and it will generate both the dd and input tags.
@Iacovos Constantinou Thank you!
I use …
->removeDecorator(‘label’)
->removeDecorator(‘HtmlTag’);
… stacked and it works, for hidden and hash elements.
I don’t care if it does not validate.