Wednesday, July 1, 2009

Zend Form display groups decorators example

In my previous posts I discussed creation of zend form and decorators. These features are very helpful and quite easy to use.
However to give better look and feel to your form you will need to study Zend From display groups.
Display groups are used to group form elements for display purposes, so they give new look and feel to your forms. The name
of the form elements remain the same however elements in the same group are render together.
The most common use case would be to use display groups to put your form element in different field sets.

Your may require similar type of layout for your form in your application. It is very simple to achieve this layout. The code

is given bellow.
Before explaining the code I would like to tell you that Zend Form uses Zend_Form_ DisplayGroup class define in
Zend/Form/DisplayGroup.php for display groups.
This class contains all the necessary attributes and methods to handle Zend Form display groups.
<?php
class SimpleForm extends Zend_Form
{
public function __construct($options = null)
{

parent::__construct($options);
$countryList=array('USA','UK');
$firstName = $this->createElement('text', 'firstName');
$firstName->setLabel('First Name')
->setAttrib('size',25)
->addValidator('StringLength', false,array(3,50))
->setValue('')
->setRequired(true);

$lastName = $this->createElement('text', 'lastName');
$lastName->setLabel('Last Name:')
->setAttrib('size', 25)
->addValidator('StringLength', false,array(3,50))
->setValue('')
->setRequired(true);

$address1 = $this->createElement('text', 'address1');
$address1->setLabel('Address1:')
->setAttrib('size', 25)
->addValidator('StringLength', false,array(3,50))
->setValue('')
->setRequired(true);

$address2 = $this->createElement('text', 'address2');
$address2->setLabel('Address2:')
->setAttrib('size', 25)
->addValidator('StringLength', false,array(3,50))
->setValue('')
->setRequired(false);

$postalCode = $this->createElement('text', 'postalCode');
$postalCode->setLabel('Postalcode:')
->setAttrib('size', 25)
->addValidator('StringLength', false,array(3,15))
->setValue('')
->setRequired(false);

$city = $this->createElement('text', 'city');
$city->setLabel('City:')
->setAttrib('size', 25)
->setValue('')
->setRequired(false)
->setAttrib('tabindex','6');

$state = $this->createElement('text', 'state');
$state->setLabel('State:')
->setAttrib('size', 6)
->setAttrib('maxlength', 2)
->setValue('')
->setRequired(false)
->setAttrib('tabindex','7');

$country = $this->createElement('select', 'country');
$country->setLabel('Country:')
->setAttrib('class','select')
->addMultiOptions($countryList)
->setRequired(false);

$phone = $this->createElement('text', 'phone');
$phone->setLabel('Phone:')
->setAttrib('size', 25)
->setAttrib('maxlength', '25')
->setValue('')
->setRequired(true);

$emailAddress = $this->createElement('text', 'emailAddress');
$emailAddress->setLabel('Email:')
->setAttrib('size', 25)
->addValidator('StringLength', false,array(5,50))
->addValidator('EmailAddress')
->setValue('')
->setRequired(true);

$website = $this->CreateElement('text','website');
$website->setLabel("Website:")
->setAttrib('size', 25);

$userName = $this->createElement('text', 'userName');
$userName->setLabel('Username:')
->setAttrib('size', 25)
->addValidator('StringLength', false,array(5,50))
->setValue('')
->setRequired(true);

$password = $this->createElement('password','password');
$password->setLabel('Password:')
->setAttrib('size', 25)
->addValidator('StringLength', false,array(3,50))
->setRequired(true)
->setValue('')
->setIgnore(false);

$confirmPassword = $this->createElement('password','confirmPassword');
$confirmPassword->setLabel('Confirm Password:')
->setAttrib('size', 25)
->addValidator('StringLength', false,array(3,50))
->setRequired(true)
->setValue('')
->setIgnore(false);

$this->addElements( array (

$firstName,
$lastName,
$address1,
$address2,
$postalCode,
$city,
$state,
$country,
$phone,
$emailAddress,
$website,
$userName,
$password,
$confirmPassword
)
);

$this->addDisplayGroup(array(

'firstName',
'lastName',
'userName',
'address1',
'address2',
'postalCode',
'city',
'state',
'country',
'phone'

),'contact',array('legend' => 'Contact Information'));

$contact = $this->getDisplayGroup('contact');
$contact->setDecorators(array(

'FormElements',
'Fieldset',
array('HtmlTag',array('tag'=>'div','style'=>'width:50%;;float:left;'))
));

$this->addDisplayGroup(array(

'password',
'confirmPassword',

),'pass',array('legend' => 'Password'));

$pass = $this->getDisplayGroup('pass');
$pass->setDecorators(array(

'FormElements',
'Fieldset',
array('HtmlTag',array('tag'=>'div','openOnly'=>true,'style'=>'width:48%;;float:right'))
));

$this->addDisplayGroup(array(

'emailAddress',
'website',

),'web',array('legend' => 'Web Information'));

$web = $this->getDisplayGroup('web');
$web->setDecorators(array(

'FormElements',
'Fieldset',
array('HtmlTag',array('tag'=>'div','closeOnly'=>true))
));

$this->setDecorators(array(
'FormElements',
array('HtmlTag',array('tag'=>'div','style'=>'width:98%')),
'Form'
));

}
}


The code above is very simple to explain.
we are extending Zend_Form as usual,define our constructor and call parent class constructor. You can override init() method
of Zend_Form and place the above code in that function.
Next we create elements and add to the form using addElements() method.
Next lines are important. beacuse we are define our display groups and decorators in these lines.
$this->addDisplayGroup(array(

'firstName',
'lastName',
'userName',
'address1',
'address2',
'postalCode',
'city',
'state',
'country',
'phone'

),'contact',array('legend' => 'Contact Information'));

$contact = $this->getDisplayGroup('contact');
$contact->setDecorators(array(

'FormElements',
'Fieldset',
array('HtmlTag',array('tag'=>'div','style'=>'width:50%;;float:left;'))
));


Zend_Form setDisplayGroup() method take three arguments i.e. array of elements, name of display group and optional parameters
array.
we are giving our first display group a name “contact”.
then we get reference of display group and set its decorators using setDecorators method of the DisplayGroup class.
thats is. enjoy.
if you need extra information, post your questions.

21 comments:

  1. Your form has no submit button. When I add one it renders above the DisplayGroups. is it necessary to put the submit button in a DisplayGroup of its own?

    ReplyDelete
  2. Great post! We are looking for a nifty way to put (some) fields next to eachother... any ideas on that?

    ReplyDelete
  3. Fantastic article - I'm just starting with Zend Framework and this is exactly what I was looking for. Thanks for sharing!

    ReplyDelete
  4. Is there a way of adding style information without getting the displaygroup object? Can I do something similar to below or do I have to get the displaygroup object first and then setting the "Options" on that? The below is just an example and does not work though.

    $this->addDisplayGroup(array(

    'firstName',
    'lastName',
    'userName',
    'address1',
    'address2',
    'postalCode',
    'city',
    'state',
    'country',
    'phone'

    ),'contact',array('style' => 'display:none'));

    ReplyDelete
  5. Thank you for your helpful tutorials. I am somewhat new to Zend Framework and can always find something new to try in your posts. Today I decided to test out the displayGroup method and had success with some simple dojo text boxes. I would also like to put a group of checkboxes within a displayGroup. I have now tried several different approaches but everytime I am only able to get the first checkbox label into the displayGroup. The checkbox itself displayed outside of the group as well as the other checkbox labels and checkboxes. Any ideas?

    ReplyDelete
  6. QUESTION, PROBLEM, QUESTION....

    I've added a textbox in one of the groups dynamically and works ok. But, if I submit the form and one or more fields return a validation error, then my textbox loose the group and appears at the end of the form. Any ideas???

    ReplyDelete
  7. you should never override the __construct method since the framework provides an init() method for setup.

    also, you appear to have completely ignored the fluent interface of basically all form components. For example, a poster above asked about how to add styles without separately getting the display group object. Rough example:

    public function init()
    {
    // ...
    $displayGroup = $this->addDisplayGroup()
    ->setName('contact')
    ->addElements(array(...))
    ->setLegend('Contact Information')
    ->setAttribs(array('style' => '...'))
    ->setDecorators(...);
    }

    ReplyDelete
  8. Thanks for this, far more versatile and agile then declaring something like...

    // Add the firstname element
    $this->addElement('text', 'firstname2', array(
    'label' => 'First name2:',
    //'required' => true,
    'decorators' => $this->elementDecorators,
    'class' => 'wide'/*,
    'validators' => array(
    array('validator' => 'StringLength', 'options' => array(0, 20), 'messages' => 'mo')
    //->addValidator('NotEmpty', true, array('messages' => array('isEmpty' => 'Empty!')));
    )*/
    ));

    $this->firstname->addValidator('notEmpty', true, array('messages' => array(
    'isEmpty' => 'Please enter your First name',
    )));

    // Add the surname element
    $this->addElement('text', 'surname2', array(
    'label' => 'Surname2:',
    //'required' => true,
    'decorators' => $this->elementDecorators,
    'class' => 'wide'/*,
    'validators' => array(
    array('validator' => 'StringLength', 'options' => array(0, 20))
    )*/
    ));

    $this->surname->addValidator('notEmpty', true, array('messages' => array(
    'isEmpty' => 'Please enter your Surname',
    )));


    and so on, your approch is far better!

    Many many thanks!

    ReplyDelete
  9. The post is really helpful for me. Thank You but I got one problem. How can i define color for legend in the zend framework without doing it from css? Thankx in advance.

    ReplyDelete
  10. This is exactly what I was looking for, thanks!

    ReplyDelete
  11. Boss,
    You are Away some............... Please Keep posting like this

    ReplyDelete
  12. Very helpful article. Just what I was looking for. Thank you...

    ReplyDelete
  13. Hi Gurus,

    Sorry if it sounds stupid but I have a concern about how to render two inputs in single line inside the fieldset.

    Thanks in advance.

    ReplyDelete
  14. Still helpful :) thanks a lot.

    ReplyDelete
  15. Thanks a lot man, I just stack on how using display Groups and you helped me a lot. Keep up the good articles.

    ReplyDelete
  16. Thanks a lot . But i got a problem that is, have to validate fields belongs to each display group on some button click say next or previous buttons in display group itself, those are used to navigate between different display groups in a form and used 5 tabs because of hell number of fields in form and organised data fields.

    Whenever i click on submit button that is in last tab with invalid data entered, by default it will take to first tab even the invalid data entries are in 2nd or 3rd or 4th tab so user has to navigate between tabs to search where actually invalid data entries.

    can you please suggest validating each display group before user leaves that particular display group

    ReplyDelete
  17. The best! Even Stackoverflow couldn't solve my problem. Thank you for this!

    ReplyDelete
  18. Hi I have a question is it possible to do this with Zend 2 and how?

    ReplyDelete
  19. Nice! Thanks a lot!

    ReplyDelete