multimodelform

Handling of multiple records and models in a form
72 followers

This extension allows to work with multiple records and different models in a edit form. It handles clientside cloning and removing input elements/fieldsets and serverside batchInsert/Update/Delete.

Creating forms with this functionality is a 'tricky' workaround in views and controllers. This widget should do the main part of the work for you. It can be useful for order/registration forms ...

See:

'multimodelform' could be an updated version of my extension jqrelcopy, but because of the different approach jqrelcopy will stay as lightweight extension for other needs. If you don't have to work with models take a look at jqrelcopy.

Note

Not every form input element is supported.

You can use the a few basic CFormInputElement (text, textarea, dropdownlist, ...) and some widgets (CJuiDatePicker, see below) but at the moment

  • checkbox: you have to use a checkboxlist instead (see below and the demo.3.2)
  • Autocomplete ...

are not supported.

Features

  • Clientside Clone/Remove form elements / fieldsets with the jQuery plugin http://www.andresvidal.com/labs/relcopy.html
  • Simple handling of the submitted form data in the controllers create/update action
  • Simultanous editing multiple records and models (master/detail...)
  • Supports validation and error summary for each model/record
  • Autogenerating input form elements with the 'form builder' functionality of Yii

New since v2.0

  • tableView (= gridView)
  • Support for cloning widget form elements: CJuiDatePicker, extension datetimepicker

Imporant note when upgrading to v2.1

  • MultiModelForm::validate and MultiModelForm::save have now the same parameterlist, so you have to add 'deletedItems' as third parameter (see Usage Validate/Save)
MultiModelForm::save($model,$validatedItems,$deleteItems,$masterValues)

Requirements

  • Yii 1.1.6+

Usage

  • Extract the files under .../protected/extensions

  • Master/Detail example:

Assume you have two models 'group' (id, title) and 'member' (id, groupid, firstname,lastname,membersince). The id attribute is the autoincrement primary key.

  • Generate the models 'Group' and 'Member' with gii. For testing the error summary set the members firstname/lastname as required in the rules.

  • Generate the 'GroupController' and the group/views with gii. You don't need to create a 'MemberController' and the member views for this example.

  • Change the default actionUpdate of the GroupController to

public function actionUpdate($id)
{
    Yii::import('ext.multimodelform.MultiModelForm');
 
    $model=$this->loadModel($id); //the Group model
 
    $member = new Member;
    $validatedMembers = array(); //ensure an empty array
 
    if(isset($_POST['Group']))
    {
        $model->attributes=$_POST['Group'];
 
        //the value for the foreign key 'groupid'
        $masterValues = array ('groupid'=>$model->id);
 
        if( //Save the master model after saving valid members
            MultiModelForm::save($member,$validatedMembers,$deleteMembers,$masterValues) &&
            $model->save()
           )
                $this->redirect(array('view','id'=>$model->id));
    }
 
    $this->render('update',array(
        'model'=>$model,
        //submit the member and validatedItems to the widget in the edit form
        'member'=>$member,
        'validatedMembers' => $validatedMembers,
    ));
}
  • Change the code of actionCreate like this
public function actionCreate()
{
    Yii::import('ext.multimodelform.MultiModelForm');
 
    $model = new Group;
 
    $member = new Member;
    $validatedMembers = array();  //ensure an empty array
 
    if(isset($_POST['Group']))
    {
        $model->attributes=$_POST['Group'];
 
        if( //validate detail before saving the master
            MultiModelForm::validate($member,$validatedMembers,$deleteItems) &&
            $model->save()
           )
           {
             //the value for the foreign key 'groupid'
             $masterValues = array ('groupid'=>$model->id);
             if (MultiModelForm::save($member,$validatedMembers,$deleteMembers,$masterValues))
                $this->redirect(array('view','id'=>$model->id));
            }
    }
 
    $this->render('create',array(
        'model'=>$model,
        //submit the member and validatedItems to the widget in the edit form
        'member'=>$member,
        'validatedMembers' => $validatedMembers,
    ));
}
  • Change the renderPartial in views/group/create.php and update.php to transfer the parameters $member and $validatedMembers to the _form.php
echo $this->renderPartial('_form', array('model'=>$model,
                          'member'=>$member,'validatedMembers'=>$validatedMembers));
  • Change the generated code of the GroupController's form view (views/group/_form.php).
<div class="form wide">
 
<?php $form=$this->beginWidget('CActiveForm', array(
        'id'=>'group-form',
        'enableAjaxValidation'=>false,
)); ?>
 
<p class="note">Fields with <span class="required">*</span> are required.</p>
 
<?php
    //show errorsummary at the top for all models
    //build an array of all models to check
    echo $form->errorSummary(array_merge(array($model),$validatedMembers));
?>
 
<div class="row">
    <?php echo $form->labelEx($model,'title'); ?>
    <?php echo $form->textField($model,'title'); ?>
    <?php echo $form->error($model,'title'); ?>
</div>
 
<?php
 
// see http://www.yiiframework.com/doc/guide/1.1/en/form.table
// Note: Can be a route to a config file too,
//       or create a method 'getMultiModelForm()' in the member model
 
$memberFormConfig = array(
      'elements'=>array(
        'firstname'=>array(
            'type'=>'text',
            'maxlength'=>40,
        ),
        'lastname'=>array(
            'type'=>'text',
            'maxlength'=>40,
        ),
        'membersince'=>array(
            'type'=>'dropdownlist',
            //it is important to add an empty item because of new records
            'items'=>array(''=>'-',2009=>2009,2010=>2010,2011=>2011,),
        ),
    ));
 
$this->widget('ext.multimodelform.MultiModelForm',array(
        'id' => 'id_member', //the unique widget id
        'formConfig' => $memberFormConfig, //the form configuration array
        'model' => $member, //instance of the form model
 
        //if submitted not empty from the controller,
        //the form will be rendered with validation errors
        'validatedItems' => $validatedMembers,
 
        //array of member instances loaded from db
        'data' => $member->findAll('groupid=:groupId', array(':groupId'=>$model->id)),
    ));
?>
 
<div class="row buttons">
    <?php echo CHtml::submitButton($model->isNewRecord ? 'Create' : 'Save'); ?>
</div>
 
<?php $this->endWidget(); ?>
 
</div><!-- form -->

Usage Validate/Save

The behavior has changed in v2.1. In older versions 'save' always exceuted 'validate' internally. 'validate' means to recreate all items from the form data. So there was no chance to alter the attributes of the models in $validatedItems between calling 'validate' and 'save'.

No you can do something like this.

//validate formdata and populate $validatedItems/$deleteItems
if (MultiModelForm::validate($model,$validatedItems,$deleteItems,$masterValues)) 
{
 
 //... alter the model attributes of $validatedItems if you need ... 
 
 //will not execute internally validate again, because $validatedItems/$deleteItems are not empty
 MultiModelForm::save($model,$validatedItems,$deleteItems,$masterValues);
}

But you can still use 'save' without extra validate before. Validation will be internally done when $validatedItems/$deleteItems are empty.

$validatedItems=array();
 
if(isset($_POST['FORMDATA']) && MultiModelForm::save($model,$validatedItems,$deleteItems,$masterValues)) 
{
  //... validation and saving is ok ...
  //redirect ...
}
 
//No POST data or validation error on save 
$this->render ...

Usage tableView

Set the property 'tableView'=>true.

$this->widget('ext.multimodelform.MultiModelForm',array(
       ...
        'tableView' => true,
     //'tableFootCells' => array('footerCol1','footerCol2'...), //optional table footer
       ...
    ));

Usage widget form elements

Yii allows type='AWidget' as form element. So in your form config array you can use:

array( 
   'elements'=>array(
         ....
          'lastname'=>array(
            'type'=>'text',
            'maxlength'=>40,
            ),
 
            'dayofbirth'=>array(              
                'type'=>'zii.widgets.jui.CJuiDatePicker',
                'language'=>'de',
                'options'=>array(
                    'showAnim'=>'fold',
                ),
           ...                      
        )
    )

This however needs a javascript workaround on cloning the date element. We have to assign the CJuiDatePicker functionality to the cloned new element.

Since v2.0 there are the properties jsBeforeClone,jsAfterClone,jsBeforeNewId,jsAfterNewId available where javascript code can be implemented. Use 'this' as the current jQuery object.

For CJuiDatePicker and the extension extension datetimepicker there are predefined functions available, so it's easy to make cloning date fields work.

You have to assign the property 'jsAfterNewId' to the prepared code.

Assume your form definition elements are defined in the array $formConfig. 'afterNewIdDatePicker' reads the options from the specified element and adds the 'datepicker':

$this->widget('ext.multimodelform.MultiModelForm',array(
       ...
         // 'jsBeforeNewId' => "alert(this.attr('id'));", 
        'jsAfterNewId' => MultiModelForm::afterNewIdDatePicker($formConfig['elements']['dayofbirth']),
       ...
    ));

Now cloning of the field 'dayofbirth' should work.

Support for 'datetimepicker' is analog (afterNewIdDateTimePicker).

For other widgets you have to find out the correct javascript code on cloning. Please let me know if you have found a javascript code for other widgets.

Note: I couldn't get Autocomplete working. Maybe one of you has an idea (see afterNewIdAutoComplete in MultiModelForm.php).

Sortable fieldsets (since v3.0)

Set the property 'sortAttribute' to your db-field for sorting (should be an integer) if you want to order the items by drag/drop manually. Uses jQuery UI sortable, but works only when 'tableView' is false. See the demo.

$this->widget('ext.multimodelform.MultiModelForm',array(
       ...
        'sortAttribute' => 'position', //if assigned: sortable fieldsets is enabled
       ...
    ));

Property showAddItemOnError (since v3.0)

Regarding to requests and workarounds in the forum topic:

A user should not be able to add/clone items, when in error mode (model rules not passed successfully). Now you can set the property $showAddItemOnError to false to enable this behavior. See the demo.

Properties allowAddItem and allowRemoveItem (since v3.1)

For example, if you only want to allow an admin user to add new items or remove items, you can use these properties to display the addlink and the removelinks

$this->widget('ext.multimodelform.MultiModelForm',array(
       ...
       'allowAddItem' => Yii::app()->user->isAdmin(),
       'allowRemoveItem' => Yii::app()->user->hasRole('admin'),
       ...
    ));

Checkboxes

Unfortunatly the basic checkbox is not supported, because it's not so easy to handle (see the comments in the forum).

But if you need checkboxes, you can use the checkboxlist - see the demo.3.2 If you only need a single checkbox you can set the item-data to an array with one item.

In the view / formConfig:

$memberFormConfig = array(
          'elements'=>array(
 
             ...
            'flags'=>array(
              'type'=>'checkboxlist',
              'items'=>array('1'=>'Founder','2'=>'Developer','3'=>'Marketing'), //One single checkbox: array('1'=>'Founder')
              ),
        ));

In the model you have to convert array <-> string on saving/loading - see the Member model in the demo

/*
     * Convert the flags array to string
     */
    public function beforeSave()
    {
        if(parent::beforeSave())
        {
            if(!empty($this->flags) && is_array($this->flags))
                $this->flags = implode(',',$this->flags);
 
            return true;
        }
 
        return false;
    }
 
   /*
    * Convert the flags string to array
    */
   public function afterFind()
   {
       $this->flags = empty($this->flags) ? array() : explode(',',$this->flags);
   }

Bootstrap

Set the property 'bootstrapLayout'=true if you use Twitters Bootstrap CSS or one of the Yii bootstrap extensions.

The formelements/labels will be wrapped by 'control-group', 'controls', ... so that the multimodelform should be displayed correct.

Notes

  • Take a look at MultiModelForm.php for more options of the widget.

  • The widget never will render a form begin/end tag. So you have always to add $this->beginWidget('CActiveForm',...) ... $this->endWidget in the view.

  • The implementation of the class MultiModelEmbeddedForm needs review in upcoming Yii releases. In 1.1.6+ the only output of CActiveForm.init() and CActiveForm.run() is the form begin and end tag.

  • The extension should work for non activerecord models too: instances of CModel, CFormModel...

  • You can add more widgets for rendering other models in the form view. Take care to assign a unique widget id when adding more multimodelform widgets.

  • Use Yii::app()->controller->action->id or $model->scenario to generate different forms for your needs (readonly fields on update ...)

Ressources

Changelog

  • v4.5
    • added support for composite pk
    • new property 'renderForm' allows a custom 'MultiModelRenderForm'
    • changed update behavior: loads the record from the db by findByPK before update (like default behavior of actionUpdate in a controller)
  • v4.1
    • Bugfix: rendering tableView with Bootstrap layout
  • v4.0

    • Bugfix: two remove columns in tableview when hideCopyTemplate=true. Many thanks to shoemaker. See this forum topic
    • Support for Twitters Bootstrap
    • New property 'addItemAsButton' to show a button 'Add item' instead of a link.
    • Minor bugfix in js-code
  • v3.3

    • Correct support for the options['limit'] property from relcopy.js. See property limitText.
  • v3.2
    • New property 'hideCopyTemplate' (default:true), means that the empty copyTemplate is not visible
    • New demo with CheckBoxList
  • v3.1
    • New: Added properties $allowAddItem and $allowRemoveItem
    • Bugfix: hidden fields in the Tableheader no more displayed
    • Bugfix: Strings as elements formconfig (htmlttags,CFormStringElement) have not been rendered
  • v3.0

    • New: Added sortable feature for fieldsets (not in table view)
    • New: Property $showAddItemOnError=true; Show 'Add item' link and empty item in errormode
    • New: Demo with sortable fieldsets
    • Bugfixes: visible/hidden fields
  • v2.2.1 Bugfix: Array elements need extra 'allEmpty' check 

  • v2.2
    • Support for array elements (checkboxlist, radiolist) in form config These elements didn't work on creating only on update
  • v2.1.1
    • Bugfix - Labels of hidden elements have been rendered
  • v2.1
    • Bugfix - tableView: Hidden input have been rendered into a cell
    • Changed parameters and handling of MultiModelForm::save
  • v2.0.1 Bugfix: Tableheader displayed hidden fields too. Better internal handling of labels, now supports required '*'
  • v2.0 Added 'tableView' and support for cloning date/time widgets
  • v1.0.2 Bugfix: Detailrecord was created twice on creating master
  • v1.0.1 Bugfix 'Undefined Index','Undefined variable' when error_reporting(E_ALL) is set

Total 20 comments

#13012 report it
Joze Senegacnik at 2013/04/27 10:24pm
Rendering AddItem (add nw record) link at the bottom of the MultiModelForm

I have slightly changed the code of Multimodelform extension in order to render the links for adding records on the bottom of the form what is much more logical position. For this purpose I simply used the trick to store the value of the generated link in a variable instead of echoing it to the output and then I render it within 'tableFootCells' property. Here is the code for changed function renderTableBegin which is part of the Multimodelform extension. In MultiModelForm.php file replace the renderTableBegin function with the following:

/**
     * Render the top of the table: AddLink, Table header
     */
    public function renderTableBegin($renderAddLink)
    {
        $renderForm = $this->renderForm;
        $form = new $renderForm($this->formConfig, $this->model);
        $form->parentWidget = $this;
        $savedLink="";
 
        //add link as div
        if ($renderAddLink)
        {
            $addLink = $form->getAddLink();
            //echo CHtml::tag('div', array('class' => self::CLASSPREFIX . 'addlink'), $addLink);
            $savedLink = CHtml::tag('div', array('class' => self::CLASSPREFIX . 'addlink'), $addLink);
        }
 
 
        $tableHtmlOptions = array_merge(array('class' => self::CLASSPREFIX . 'table'), $this->tableHtmlOptions);
 
        //table
        echo CHtml::tag('table', $tableHtmlOptions, false, false);
 
        //thead
        $form->renderTableHeader();
 
        //add saved link to tableFootCells
        $this->tableFootCells = array_merge(array($savedLink), $this->tableFootCells);
 
        //tfoot
        if (!empty($this->tableFootCells))
        {
            $cells = '';
            foreach ($this->tableFootCells as $cell)
            {
                $cells .= CHtml::tag('td', array('class' => self::CLASSPREFIX . 'cell'), $cell);
            }
 
            $cells = CHtml::tag('tr', array('class' => self::CLASSPREFIX . 'row'), $cells);
            echo CHtml::tag('tfoot', array(), $cells);
        }
 
        //tbody
        $tbodyOptions = $this->isSortable() ? array('id' => $this->getSortSelectorId()) : array();
        echo CHtml::tag('tbody', $tbodyOptions, false, false);
 
    }
#13011 report it
Joze Senegacnik at 2013/04/27 09:48pm
Multimodelform using ESelect2 extension

I found that the ESelect2 extension was ideal for the requirements on the recent project. It supports key=>value pair integration. However, there was problem when ESelect2 widget was copied with relcopy. So after getting some great advices from the author of the combobox support (see below post), I was able to get things working.

Here is the code which should be included in MultiModelForm extension (MultiModelFormp.php)

public static function afterNewIdSelect2($options)
{
    return "if ( this.hasClass('select2-offscreen')){
                var mmfSelect2Clone  = this.clone();
                var mmfSelect2Parent = this.parent();
                mmfSelect2Parent.empty();
                mmfSelect2Parent.append(mmfSelect2Clone);
                                //now initialize the copied widget
                mmfSelect2Clone.select2({$options});
            }";
}

And in the _form.php file the definitions are like the following:

$opazovanjeFormConfig = array(
        'showErrorSummary' => true,
        'elements'=>array(
            'id_vrste'=>array(
                    'type'=>'ext.select2.ESelect2',
                    'model'=>$opazovanje,
                    'attribute'=>'id_vrste',
                    'data'=>CHtml::listData(Vrste::model()->findAll(), 'id_vrste', 'vrsta_slo'),
                    'options'=>array(
                        'width'=>180,
                        'placeholder' => '<Placeholder...>',),
                    'visible' => true,
                    ), 
            'id_statusa_opazovanja'=>array(
                'type'=>'ext.select2.ESelect2',
                'model'=>$opazovanje,
                'attribute'=>'id_statusa_opazovanja',
                'data'=>CHtml::listData(VStatusiOpazovanja::model()->findAll(), 'id_statusa', 'koda'),
                'options'=>array(
                        'width'=>180,
                        'placeholder' => '<Placeholder...>',
                        'visible' => true,
                        ),
                ),
            'stevilo'=>array(
                'type'=>'text',
                'maxlength'=>6,
                'default'=>null,
                'attributes' => array('class'=>'span1', 'style'=>'text-align: right'),
                ),
            ... 
        )
    );
 
    $select2Options="{'formatNoMatches':function(){return \"No matches found\";},'formatInputTooShort':function(input,min){return \"Please enter \"+(min-input.length)+\" more characters\";},'formatInputTooLong':function(input,max){return \"Please enter \"+(input.length-max)+\" less characters\";},'formatSelectionTooBig':function(limit){return \"You can only select \"+limit+\" items\";},'formatLoadMore':function(pageNumber){return \"Loading more results...\";},'formatSearching':function(){return \"Searching...\";},'width':180}";
 
 
    $this->widget('ext.multimodelform.MultiModelForm',array(
        'id' => 'vnos-opazovanja', //the unique widget id
        'formConfig' => $opazovanjeFormConfig, //the form configuration array
        'model' =>  $opazovanje, //instance of the form model
        'hideCopyTemplate' => ($this->action->Id == 'update' ? true : false),
        'addTabIndex'=>true,
        'validatedItems' => $validatedOpazovanja,
        'data' => empty($validatedItems) ? $opazovanje->findAll(
                array('condition'=>'id_obiska=:id_obiska',
                        'params'=>array(':id_obiska'=>$model->id_obiska),
                        'order'=>'id_opazovanja', 
                     )) : null,
        'jsAfterNewId' => MultiModelForm::afterNewIdSelect2($select2Options),
        'showAddItemOnError' => false, //not allow add items when in validation error mode (default = true)
        'tableView' => true, //sortable will not work
        'fieldsetWrapper' => array('tag' => 'div', 'htmlOptions' => array('class' => 'view','style'=>'position:relative;background:#EFEFEF;')),
        'removeLinkWrapper' => array('tag' => 'div', 'htmlOptions' => array('style'=>'position:absolute; top:1em; right:1em;')),
        'addItemText'=>'<img src="images/add.png">',
        'removeText'=>'<img src="images/del.png" style="max-width: 24px;">',
));

If you need more than one ESelect2 widget per model and if they would need different configuration then you should parametrize the options for ESelect2 widget which are used in the afterNewIdSelect2 function. These options are required to properly initialize the widget after copying.

#12999 report it
Joze Senegacnik at 2013/04/27 04:48am
Preventing validation error on 'copytemplate' record when using dropdownlist or combobox or any kind of default values

The multimodelform has property to either show or hide so called 'copytemplate' record. This property is named 'hideCopyTemplate'. When one is using a dropdownlist or combobox widget which is configured for a non-empty default value (like 'P' instead of '') for this field then the multimodel form will raise validation error on required fields for the coyptemplate record because the whole 'copytemplate' record is not completely empty. This problem is occurring when the existing record is updated. The multimodelform always ads a copytemplate record which can be either visible or hidden. In order to prevent validation errors one can hack the $_POST array before it is processed in the controller's 'actionUpdate' and set the value of problematic field to ''.

In my recent project the requirement was that whenever a new record is added some of the fields which were using combobox should already have a predefined default value which can be later changed by the user.

Here is the code in which we first check that all other fields are not set and then we set the value to ''. We need to check values for all other fields in this model in order to be sure that we are setting value on the 'empty copytemplate' record.

...
if(isset($_POST['MyModel']))
{
    if ($_POST['MyModel']['MyComboboxField'][0] == '<my default value>' &&
        $_POST['MyModel']['field1'][0] == '' &&
        $_POST['MyModel']['field2'][0] == '' &&
        $_POST['MyModel']['field3'][0] == '' ){
            $_POST['MyModel']['MyComboboxField'][0] = '';       
    }
...
}

Fields, which are not displayed on the form, but need to be initialized should be initialized in the beforeValidate function. In the model one should add the follwoing code:

public function defineDefaultValues(){
    $this->field1 = 'P';
    $this->field2 = 20;
    ....
}
 
protected function beforeValidate()
{
    if($this->isNewRecord)
    {
        $this->defineDefaultValues();
        //set who has added this record
        $this->create_user_id = $this->update_user_id = Yii::app()->user->getId();
    }
    else
    {
        // not a new record, so just set last updated user id
        $this->update_user_id=Yii::app()->user->getId();
    }
    return parent::beforeValidate();
}

Don't forget to call parent::beforeValidate() at the end of your beforeValidate function.

#12616 report it
vbeiws at 2013/04/01 07:57pm
dependent dropdown type scenario issue

hi Joblo,

Thanks for nice extension, works like charm. However, I am facing a problem with dependent dropdown type of scenario. I thought may be you can help. the scenario is like this: I have one master form like group in example and other sub form(which will be repeated) for one group. now, this sub form has four fields: items, costp, salep. I get the list of items in a dropdown from db no problem, but now to get the costp of selected item in dropdown, I write an Ajax call and get the data. However, it works only when the sub form is generated once, if I try to create second sub form and do the same, nothing happens due to conflicted id of sub form element. Can you please help? I will appreciate any help.

Regards, VB

#12266 report it
__construct() at 2013/03/10 03:30pm
Combobox support

Hello again.

I'm also added a combobox widget support. ( http://www.yiiframework.com/extension/combobox )

In my application I use a Toph's fork of combobox: https://github.com/kanyuga/yii-combobox (because this fork supports key=>value separation).

But it shoud work with main branch too (not tested).

Combobox plugin needs a small bugfix to work with multimodelform ( see my comment here: http://www.yiiframework.com/extension/combobox#c12257 )

Here is the code:

/**
     * Support for EJuiComboBox
     * 
     * @contributor Smirnov Ilya php1602agregator[at]gmail.com
     * @param array $element
     * @param bool  $allowText
     * @return string
     */
    public static function afterNewIdComboBox($element, $allowText=true)
    {
        $options = array();
        if ( $allowText )
        {
            $options['allowText'] = true;
        }
 
        $jsOptions = CJavaScript::encode($options);
 
        return "if ( this.attr('type') == 'text' && this.hasClass('ui-autocomplete-input') ) 
            {
                var mmfComboBoxParent   = this.parent();
                // cloning autocomplete and select elements (without data and events)
                var mmfComboBoxClone    = this.clone();
                var mmfComboSelectClone = this.prev().clone();
 
                // removing old combobox
                mmfComboBoxParent.empty();
                // addind new cloden elements ()
                mmfComboBoxParent.append(mmfComboSelectClone);
                mmfComboBoxParent.append(mmfComboBoxClone);
 
                // re-init autocomplete with default options
                mmfComboBoxClone.combobox({$jsOptions});
            }
 
            if ( this.attr('type') == 'button' )
            {// removing old combobox button
                this.remove();
            }";
    }
#12261 report it
__construct() at 2013/03/10 12:54pm
Breaking news! Autocomplete FINALLY WORKS!

Good news, everyone!

Yeah, I made it! :)

The problem was an incorrect element copying: you shoud copy "autocomplete" element without data and events. (more information here: http://api.jquery.com/clone/ )

Now, if you want to use autocomplete elements in your form - your code shoud look like this:

/**
     * Support for CJuiAutoComplete. 
     *
     * @contributor Smirnov Ilya php1602agregator[at]gmail.com
     * @param array $element
     * @return string
     */
    public static function afterNewIdAutoComplete($element)
    {
        $options = isset($element['options']) ? $element['options'] : array();
        if (isset($element['sourceUrl']))
            $options['source'] = CHtml::normalizeUrl($element['sourceUrl']);
        else
            $options['source'] = $element['source'];
 
        $jsOptions = CJavaScript::encode($options);
 
        return "if ( this.hasClass('ui-autocomplete-input') )
            {
                var mmfAutoCompleteParent = this.parent();
                // cloning autocomplete element (without data and events)
                var mmfAutoCompleteClone  = this.clone();
 
                // removing old autocomplete element
                mmfAutoCompleteParent.empty();
                // re-init autocomplete with default options
                mmfAutoCompleteClone.autocomplete({$jsOptions});
 
                // inserting new autocomplete
                mmfAutoCompleteParent.append(mmfAutoCompleteClone);
            }";
    }

Please update the extension.

#11969 report it
reburg at 2013/02/16 05:24pm
column header is missing when using dropdownlist

When using a dropdownlist in a column of the multimodelform, then the header of that column is not displayed in tableview.

I found the solution: The element's property 'visible'=>true must be explicitly set for an element of type dropdownlist.

Greetings from Austria, Ferdinand

#11862 report it
Kochiro at 2013/02/08 04:34am
Suggestion

Thank you for the great extension. I'm using a highly modified version of this in my project based on v4.5. One thing I've changed that made it a lot easier to work with is to change all the "echo" statements to compile into variables and return variables instead of echoing them directly. Then at the end of the MultiModelForm.run() function echo out the variable one time.

#11555 report it
Joblo at 2013/01/20 01:30pm
One model

I think you have two models: Employee and Task But if you only want to display the tasks of a employee/user you can set the masterValue(s) before save in the controller.

Please use this forum topic for more diskussion.

#11476 report it
Lukatone at 2013/01/15 06:31pm
Clone form elements

Hi, i would like to know if i can use this extension with just one model. I have Employees that register their tasks daily and i need to save all of them in a batch. The form looks like a table where the user can clone the form elements depending on the amount of tasks he needs to load. I´m using yii boostrap. I would appreciate any comment. Thanks!

#11159 report it
__construct() at 2012/12/20 07:55am
copy template isn't shown without forcing "visible" option in form config

I found a bug in this exstension. Demo works fine, but I found a problem in my application: copy template and table header (when using table view) is empty by default. Clicking "add item" link adds only remove link and nothing more.

I found a solution by forcing "visible" attribute on every element in form:

public function formConfig()
    {
        return array(
            'elements'=>array(
 
                'event'=>array(
                    'type'=>'text',
                    'maxlength'=>255,
                    'visible' => true, // copy template is empty without it!
                ),
 
                'year'=>array(
                    'type'=>'dropdownlist',
                    'items'=>array(''=>'-',2009=>2009,2010=>2010,2011=>2011,),
                    'visible' => true,// copy template is empty without it!
                ),
            ));
    }

Extension ver. 4.1

#10228 report it
Jimlam at 2012/10/12 05:22am
Autocomplete

Hi Joblo,

I posted a workaround for the autocomplete feature on the extension forum. I resorted to binding a javascript code to the new id of the autocomplete element. It worked on all subsequent clones except... the 1st one. Strange!!

#10207 report it
Jimlam at 2012/10/11 03:32am
Something I am missing

Hi Joblo,

I am implementing your extension in my application. Something I do not understand: where are you defining $deleteItems and $deleteMembers?

Thanks

#10050 report it
Jimlam at 2012/10/02 06:04am
Dependent dropdown

Thanks very much for your prompt reply. Will work on it and let you know if I succeed :)

#10042 report it
Joblo at 2012/10/01 04:13pm
dependent dropdown

I have just released the cascadedropdown but didn't try it in a multimodelform.

Only an idea, have no time for testing:

Add two dropdownlists in the formconfig, add the code to cascade these inputs (master->setDependent) in the view. I don't know what happens after cloning. I think you have to add js-code to assign the cascade for the new ids again. You can try if this is possible with the 'jsAfterClone' or 'jsAfterNewId' property for that ... Take a look at the source of the MultiModelForm::afterNewIdDatePicker.

#10039 report it
Jimlam at 2012/10/01 02:30pm
Is it possible to have a dependent dropdown list among the member fields?

Thanks for this great extension. Before starting to modify the views according to my needs, I would like to know if it is possible to have among the member fields a dependent dropdown list with values which will depend on value input in a previous field among the member fields? If so, can you just give me a hint?

Many thanks

#9042 report it
vibhaJadwani at 2012/07/16 03:48am
Great !

Just loved it! Many Thanks.

#8328 report it
Kunbolat at 2012/05/25 11:14pm
how looping multimodelform

test How create Controller and view for online test using multimodelform. Please help me

#7301 report it
fr0d0z at 2012/03/12 01:28pm
Wouldn't it be simpler / cleaner

to add this to your config file:

// autoloading model and component classes
'import'=>array(
    'application.models.*',
    'application.components.*',
......
    'ext.multimodelform.MultiModelForm',

instead of using:

Yii::import('ext.multimodelform.MultiModelForm');

in your controller? Yii will handle the autoloading as needed, so using the actual import line seems redundant / leads to messier controllers ...

#7095 report it
Th3 R00ST3R at 2012/02/23 12:10pm
Thanks for the forum topic link.

Didn't know there was one..

Leave a comment

Please to leave your comment.

Create extension