2009-02-23 - Let's be more dynamic with sfForm

Introduction

sfForm introduces a nice way to handle form, once you get it you cannot switch to old school form handling. But the current sfForm implementation is quite static as you cannot mix some logic : update form's values depends on user action.

You can add some logic layer by calling Ajax actions to retrieves values and code some weird javascript to update values. But this does not seems to be too logical, as the form should handle all data management for its widgets.

The swToolboxPlugin implements a proof of concept of a dynamic form where data management is handled by the form itself.

Demo page here : http://rabaix.net/labs/dynamic-form-v1.

Installation

1. Install swToolboxPlugin
 - svn version at http://svn.symfony-project.com/plugins/swToolboxPlugin/sf1.2/trunk/
 - there is not package version for now

2. enabled the swToolbox module in your settings.yml file

3. install jQuery library and make sure you publish assets bundle with the plugin

3. clear your cache

Usage

Let's start by a piece of code where a nested form (DynamicNestedForm_V1) is created from the main form (DynamicForm_V1).

class DynamicNestedForm_V1 extends sfForm
{
  public function configure()
  {
    $this->widgetSchema['contact'] = new sfWidgetFormInput;
    $this->widgetSchema['information'] = new sfWidgetFormInput;
  }
}

class DynamicForm_V1 extends sfForm
{
  public function configure()
  {
    $this->widgetSchema['client_id'] = new sfWidgetFormSelect(array(
      'choices' => array(0 => 'Select a client', 1 => 'Client 1', 2 => 'Client 2', 3 => 'Client 3'))
    );
   
    $this->widgetSchema['paiement_id'] = new sfWidgetFormSelect(array(
      'choices' => array(1 => 'Paiement 1', 2 => 'Paiement 2', 3 => 'Paiement 3'))
    );
   
    $this->widgetSchema['address1'] = new sfWidgetFormInput;
    $this->widgetSchema['address2'] = new sfWidgetFormInput;
    $this->widgetSchema['address3'] = new sfWidgetFormInput;
    $this->widgetSchema['city'] = new sfWidgetFormInput;
    $this->widgetSchema['postcode'] = new sfWidgetFormInput;
   
    $this->widgetSchema['status'] = new sfWidgetFormSelectRadio(array(
      'choices' => array(1 => 'ok', 2 => 'not ok')
    ));
   
    $this->embedForm('nested_form', new DynamicNestedForm_V1);
   
    swToolboxFormHelper::updateFormElement($this, array(
      'field' => 'client_id'
    ));
  }

  public function getDynamicValues($widgetSchema, $field)
  {   
    if($field == 'client_id')
    {
      return array(
        'address1'    => 'Addresse line 1 client '.$this->getDefault('client_id'),
        'address2'    => 'Addresse line 2 client '.$this->getDefault('client_id'),
        'paiement_id' => (int)$this->getDefault('client_id'),
        'nested_form' => array(
          'contact' => new swFormDynamicResult($this->getDefault('client_id'), array(
            'widget' => ($this->getDefault('client_id') > 0 ? new sfWidgetFormChoice(array(
              'choices' => array(
                1 => 'Contact 1 - client #'.$this->getDefault('client_id'),
                2 => 'Contact 2 - client #'.$this->getDefault('client_id'),
                3 => 'Contact 3 - client #'.$this->getDefault('client_id')
              )
            )) : new sfWidgetFormInput),
          )),
          'information' => new swFormDynamicResult($this->getDefault('client_id'), array(
            'widget' => ($this->getDefault('client_id') > 0 ? new sfWidgetFormSelectRadio(array(
              'choices' => array(
                1 => 'Contact 1 - client #'.$this->getDefault('client_id'),
                2 => 'Contact 2 - client #'.$this->getDefault('client_id'),
                3 => 'Contact 3 - client #'.$this->getDefault('client_id')
              ),
            )) : new sfWidgetFormInput),
          ))
         
        )
      );
    }
  }
}

The configure method creates all standard fields, the last static call is where the event handler is set on the field client_id :

swToolboxFormHelper::updateFormElement($this, array(
  'field' => 'client_id'
));

So every time this value changes on the client side, a Ajax call is performed to the getDynamicValues method. The getDynamicValues returns an array of values that will be send back to the user.

The returned hash array must contains the name of the different form's fields as a key. If you only need to change the value you can do :

 'address1'    => 'Addresse line 1 client '.$this->getDefault('client_id')

If you need more complex logic you have to create a swFormDynamicResult object which contains the value, or you can pass a widget to be rendered. So in the current example, if the client_id is greater that 0, the form send back radio elements, else a standard text input box.

For now the swFormDynamicResult object is quite simple and handle only value and widget as options. The next step will be to add more options like : addClass, removeClass, show, hide, etc...

Conclusion

This current swToolboxFormHelper class allows to add more logic to the form, however there is still some issues due to the current sfForm implementation. Inside the getDynamicValues you work with unclean values.

Comments

comments powered by Disqus