- Features
- Notes
- Requirements
- Usage
- Usage Validate/Save
- Usage tableView
- Usage widget form elements
- Sortable fieldsets
- Checkboxes
- File fields and file upload
- Property showAddItemOnError
- Properties allowAddItem and allowRemoveItem (since v3.1)
- Bootstrap
- Property jsAfterCloneCallback
- Notes
- Ressources
- Changelog
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 ...
Find the latest releases on github
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
- tableView (= gridView)
- filefields with full upload handling and image preview in the edit form.
Notes ¶
Not every form input element is supported.
You can use the most basic CFormInputElements (text, textarea, dropdownlist, ...).
Some input widgets need a workaround with js-code after clientside cloning. Currently supported with ready to use javascript code in methods:
- CJuiDatePicker
- datetimepicker
- EJuiComboBox and CJuiAutoComplete (added by Smirnov Ilya)
Special handling:
- Checkbox: you have to use a checkboxlist instead (see below and the demo.3.2)
New in v6.0.0
- Filefield with fully handling all uploading and validating stuff
are not supported.
Requirements ¶
- Yii 1.1.6+
Usage ¶
- Extract the files under .../protected/extensions or use the composer
Master/Detail example: ¶
You can find the implemtation explained below in the demo application.
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 ¶
You can split the validate() and save() methods of the Multimodelform to modify the items before saving.
//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);
}
Of course you can 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.
There are the properties jsBeforeClone,jsAfterClone,jsBeforeNewId,jsAfterNewId available where javascript code can be implemented. Use 'this' as the current jQuery object.
For CJuiDatePicker, the extension datetimepicker, CJuiAutoComplete and EJuiComboBox 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: use afterNewIdDateTimePicker()
- CJuiAutoComplete: use afterNewIdAutoComplete()
- EJuiCombobox: use afterNewIdJuiComboBox()
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.
Sortable fieldsets ¶
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
...
));
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 instead. 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);
}
File fields and file upload ¶
MultiModelForm handles all the stuff with uploading files and images. Assigned images will be displayed as preview, files as download link in the edit form near the upload input.
Add a input element of type 'file' to the form config. It's important to add 'visible'=>true because by Yii default a file field is not safe and unsafe attributes will not be visible.
$memberFormConfig = array(
'elements'=>array(
'firstname'=>array(
'type'=>'text',
'maxlength'=>40,
),
...
'image'=>array(
'type'=>'file',
'visible'=>true, //Important!
),
));
Add the image field to the rules of your model. The image is of type file, so a CFileValidator will be used and you can add allowed types, mimetypes, maxSize etc. It's important to set 'allowEmpty'=>true otherwise, the user will be enforced to always upload a new image on updating a record.
public function rules()
{
return array(
array('firstname, lastname', 'length', 'max'=>40),
array('image', 'file', 'allowEmpty'=>true,'types'=>'jpg,gif,png'),
...
);
}
The file input field in the db must be of type string: VARCHAR(200) or similar to save the relative file path.
Default behavior ¶
The default behavior on uploading is, that mmf will save the uploaded file in the public webroot folder files/modelclass. 'modelclass' is the class of the multimodel (files/member/image1.jpg).
The relative path of the uploaded file will be assigned to the file attribute (image, ...).
Ensure the public folder files is writeable like the assets folder.
MMF takes care about unique filenames in the folder by adding index: filename-1.jpg, filename-2.jpg ... if necessary.
Change default behavior ¶
You can change this default behavior by adding callback methods to your mmf model:
Add a method mmfFileDir() to you mmf model (member,...)
public function mmfFileDir($attribute,$mmf)
{
return 'media/'.strtolower(get_class($this));
}
The $attribute can be used too, for example if there are multiple file fields in a model. $mmf is the MultiModelForm widget.
Maybe you need another behavior on file upload, for example you want to create image presets (resize, ...) on upload or save the file into the db.
Add a callback method mmfSaveUploadedFile() to the mmf model. The param $uploadedFile is a CUploadedFile instance.
public function mmfSaveUploadedFile($attribute,$uploadedFile,$mmf)
{
if(!empty($uploadedFile))
{
$uploadedFile->saveAs(...);
... resize or save to db or whatever ...
$this->$attribute = ... path to the preset or other values you like ...
}
}
If you need access to the mastermodel (group,...) inside this callback methods, you have to assign the new param 'masterModel' in the controller action.
public function actionUpdate($id)
{
$model=$this->loadModel($id); //the Group model
$member = new Member;
$validatedMembers = array(); //ensure an empty array
if(isset($_POST['Group']))
{
...
//the last param $model is the masterModel 'group'.
if(MultiModelForm::save($member,$validatedMembers,$deleteMembers,$masterValues,$_POST['Member'],$model)
...
}
...
}
Now in your callback methods you can use $groupModel=$mmf->masterModel;
Delete files after removing items ¶
MMF does not delete the uploaded files if items are removed by the user in the edit form. If you want to delete the file after removing items you have to code like below:
if (MultiModelForm::save($model,$validatedItems,$deleteItems,$masterValues))
{
foreach($deleted as $deletedModel)
{
if(!empty($deletedModel->image) && is_file($deletedModel->image))
unlink($deletedModel->image);
}
}
Attributes for file handling ¶
- fileReplaceExisting=false: if true, delete existing files with the same name on upload
- fileImagePreviewHtmlOptions=array('style' => 'max-width: 100px; max-height: 100px;') the html options for the image preview tag in the edit form
- fileLinkHtmlOptions=array('target'=>'_blank') the htmlOptions for the download link in the edit form
Property showAddItemOnError ¶
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'),
...
));
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.
Property jsAfterCloneCallback ¶
If you need to modify the cloned elements or execute js-actions after cloning, you can assign a js function(newElem,sourceElem) as callback.
- newElem: the new cloned jquery object
- sourceElem: the clone source object
The callback will be executed for all inputs of a record row.
Usage:
// a js function in your view
echo CHtml::script('function alertIds(newElem,sourceElem) {
alert(newElem.attr("id"));
alert(sourceElem.attr("id"));}'
);
$this->widget('ext.multimodelform.MultiModelForm',array(
...
'jsAfterCloneCallback'=>'alertIds',
...
));
Notes ¶
If you upgrade MultiModelForm, don't forget to delete the assets.
You can use multiple MultiModelForm widgets in a view, but the mmf models MUST be of different classes. Take care to assign a unique widget id when adding more multimodelform widgets.
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...
Use Yii::app()->controller->action->id or $model->scenario to generate different forms for your needs (readonly fields on update ...)
Ressources ¶
multimodelform on github
Forum topic multimodelform/jqrelcopy
Tutorial Using Form Builder
jQuery plugin RelCopy
Changelog ¶
- v6.0.0 Support for filefields with handling the fileupload and preview
- new parameter $initAttributes for method validate()
- new parameter $masterModel for method save()
- Added composer support / composer.json
- internal changes and minor bugfixes
- v5.0 better custom js handling
please see comments in the code; don't forget to clear assets after update
- new property 'removeOnClick' to exec js code on clicking the remove link
- new property 'jsAfterCloneCallback' - usage: see documentation above
- new property 'clearInputs' (= options['clearInputs'], default true) with bugfix on cloning values
- new property 'jsRelCopy' to use your own modified jsrelcopy js-script (placed in assets folder)
- 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
demo
How about it with a demo application?
Done...
Published demo application
Relation HAS_MANY
Nice extension.
If you define members as a HAS_MANY relation in Group, you can use it in data property of MultModelForm:
$this->widget('ext.multimodelform.MultiModelForm',array( ...... 'data' => $model->members, ));
Relations
Thanks for the hint.
At the moment I'm working most of the time with mongoDB,
not thinking about relations ... ;-)
Demo
Thank you very much!
Works_Well
Thanks for the very good Extension.
validate minimum detail
I have a question for this great extension,
I have purchase and purchase_detail model,
I don't want to create the purchase without any detail, thus minimum 1 purchase_detail for every purchase,
how can I do that? I still can save purchase with no purchase_detail with usage example from above.
thanks
Should be easy
When you call MultiModelForm::validate the 'validatedMembers' will be populated with all detailmodels submitted by the form. Some of this models can have errors (checked by the rules).
So additionally you can check if validatedMembers is an empty array.
$detailOK = MultiModelForm::validate($member,$validatedMembers,$deleteItems); if ($detailOK && empty($validatedMembers)) { Yii::app()->user->setFlash('error','No detail submitted'); $detailOK = false; } if( $detailOK && $model->save()) //save the master { //save the details and redirect } ... $this->render('create', ...)
You have to display the users flash messages in the view too.
table view
one more question :D, I tried tableView and like example above, my purchase_detail has some columns that I don't want to show/don't need user input, but tableView seems to show all the columns, its' showed only as table header and don't have input area like others that I set in formConfig, how can I remove this?
thanks again
fixed
The issue of showing hidden fields in the table header is fixed in v2.0.1.
And also fixed: Now the '*' will be added to the label of required attributes.
passing parameter
hi, I have some problems,
//...some code like example if ($validDetail && empty($validatedDetail)) { Yii::app()->user->setFlash('error','No detail submitted.'); $validDetail = false; } foreach($validatedDetail as $item) { $temp = ItemCardStock::model()->getLastData($item->item_id); if (!$temp) { Yii::app()->user->setFlash('error','Invalid request.'); $validDetail = false; } } if($validDetail && $model->save()) { $masterValues = array ('usage_id'=>$model->usage_id); foreach($validatedDetail as $item) { // ...some code $item->subtotal = $stock->subprice; // subtotal is in detail, it's value not from user input, I set the value just like that } foreach($validatedDetail as $item) print_r($item->attributes); //test print_r the value of subtotal and each has a value if(MultiModelForm::save($detail,$validatedDetail,$masterValues)) $this->redirect(array('view','id'=>$model->usage_id)); // somewhat every subtotal became NULL and }
and get error
CDbCommand failed to execute the SQL statement: SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'subtotal' cannot be null. The SQL statement executed was: INSERT INTO
item_usage_detail
(item_id
,quantity
,detail_id
,usage_id
,subtotal
) VALUES (:yp0, :yp1, :yp2, :yp3, :yp4)what I'm missing here? thanks
$validatedItem is only out parameter
What you want to do cannot work, because your $validatedDetail is an out parameter.
Take a look at line 335 - method initItems:
I have added a line to reset the $validatedItems as a bugfix v1.0.2, because detailrecords where created twice if I call 'MultiModelForm::validate' and 'MultiModelForm::save' in the usage comment above on 'actionCreate'.
So you can comment this line an see what happens.
Maybe I find another workaround like adding onBeforeSave ... to alter attribute values before saving.
great
it'll be great idea and I'm looking forward for the autocomplete feature too
Greate Extension. How about file upload?
I've tried your extension and it works great for post data and all.
But how can I get multimodelform to work with file field?
I can only render the view with file field, but could not make my model understand to process the upload file correctly.
Any suggestion?
validatedItems
Still can't get it right at line 335, since you reset the array to null, and get error if I uncomment this line
I think this validatedItems don't have to be as parameter in your function, am I correct?
you set the attributes from the form anyway,
foreach ($formData[$modelClass]['n__'] as $idx => $attributes) { ... $model->attributes = $attributes; ....}
and you change the data type of validatedItems from array to modelclass in validate(),
any solutions to set value like my comment #3925 below from controller? I'm stuck.
thanks
Update 2.1
feby_lho:
Now you should be able to alter the data between 'validate' and 'save'.
See usage validate/save above.
dinhtrung/feby_lho:
Sorry, I don't have the time yet to investigate the javascript stuff for cloning autocomplete (I have tried a few hours without a result) or filefields.
You have to play around with jsAfterNewId ... to hook into the js-code on cloning.
Take a look at the code of MultiModelForm::afterNewIdDatePicker that returns the necessary js-code. Maybe it can be easy done with filefields, not easy with autocomplete :-(
The new version really rocks
With validate and save separated, I can modify the validated items with correct data. I'll try with fileField, and report back asap.
Thanks again.
This extension really awesome.
File Upload Support
I have extend the MultimodelForm to support File upload using CUploadedFile.
[diff] diff -r 5d49e1ab920f protected/extensions/multimodelform/MultiModelForm.php --- a/protected/extensions/multimodelform/MultiModelForm.php Tue Jun 21 11:20:50 2011 +0700 +++ b/protected/extensions/multimodelform/MultiModelForm.php Tue Jun 21 20:24:17 2011 +0700 @@ -346,8 +346,9 @@ */ public function initItems(&$validatedItems,&$deleteItems, $masterValues = array(),$formData = null) { - if (!isset($formData)) + if (!isset($formData)){ $formData = $_POST; + } $result = true; $newItems = array(); @@ -371,8 +372,9 @@ $model->attributes = $attributes; foreach ($this->fileAttributes as $attribute) { - $model->attribute = CUploadedFile::getInstances($this->model, "[n__][$idx]" . $attribute); - if (count($model->attribute) == 1) $model->attribute = $model->attribute[0]; + $files = CUploadedFile::getInstances($this->model, "[n__][$idx]" . $attribute); + if (! empty($files)) $model->attribute = $files; + if (count($files) == 1) $model->attribute = $files[0]; } @@ -406,8 +408,9 @@ $model->attributes = $attributes; foreach ($this->fileAttributes as $attribute) { - $model->attribute = CUploadedFile::getInstances($this->model, "[u__][$idx]" . $attribute); - if (count($model->attribute) == 1) $model->attribute = $model->attribute[0]; + $files = CUploadedFile::getInstances($this->model, "[u__][$idx]" . $attribute); + if (! empty($files)) $model->attribute = $files; + if (count($files) == 1) $model->attribute = $files[0]; } if (!empty($masterValues)) @@ -448,10 +451,6 @@ // use the first item as reference $refAttribute = key($formData[$modelClass]); - $uploadFiles = array(); - foreach ($this->fileAttributes as $attribute) { - $uploadFiles[$attribute] = CUploadedFile::getInstances($this->model, $attribute); - } $refArray = array_shift($formData[$modelClass]); @@ -478,8 +477,10 @@ foreach ($formData[$modelClass] as $attrKey => $values){ $model->$attrKey = $values[$idx]; - if (! empty($uploadFiles[$attrKey][$idx])) { - $model->$attrKey = $uploadFiles[$attrKey][$idx]; + if (in_array($attrKey, $this->fileAttributes)){ + $files = CUploadedFile::getInstances($model, $attrKey); + if (! is_null($files)) $model->$attrKey = $files; + if (count($files) == 1) $model->$attrKey = $files[0]; } }
Let's use a model to store our file.
/** * @property CUploadedFile name : Use as the input file. * @property string title : File title * @property string path : File directory * @property string description : File description */ class FileManager extends CActiveRecord { ... protected function beforeValidate(){ if ($this->name instanceof CUploadedFile){ $this->name->saveAs($this->path . DIRECTORY_SEPARATOR . $this->name->name); } return parent::beforeValidate(); } }
And in your view, use the CForm element like this.
'elements' => array( 'name' => array( 'type' => 'file', ), 'path' => array( 'type' => 'text', 'size' => 50, 'length' => 255, ), 'title' => array( 'type' => 'text', 'size' => 20, 'length' => 80, ), 'description' => array( 'type' => 'text', 'size' => 50, 'length' => 255, ), )
Great
It would be nice if you upload the full file into the forum topic (see resource) so that I can integrate the fileupload feature in the next release.
Maybe you can add a simple full example too.
IDENTITY_INSERT issue when adding new member
Thank you for the nice extension.
I met two issues when using this, I have master class 'Team' like the 'Group' in demo and 'User' like the 'Member' describe in demo.
issue 1: my backend database is sqlserver, where the User table has id column which is auto increment identity,
with the extension, I can update user within a team,but when add a new user, the error occurs as:
CDbCommand failed to execute the SQL statement: SQLSTATE[23000]: [Microsoft][SQL Server Native Client 10.0][SQL Server]Cannot insert explicit value for identity column in table 'user' when IDENTITY_INSERT is set to OFF.. The SQL statement executed was: INSERT INTO [dbo].[user] ([username], [email], [password], [phone], [mobile], [id], [team_id]) VALUES (:yp0, :yp1, :yp2, :yp3, :yp4, :yp5, :yp6)
issue 2: How can I only show some attributes (e.g, only reuired column) in detail form instead of currently, it shows all columns of Users table
Looking forward to helping on this
issues
Please see my response in the forum
Customizing form
Hi Joe,
danke für die super Externsion.
Gibt es eine Anleitung wie man die das Aussehen / die Form der Extension anpassen kann?
Ich würde gerne meine eigene Tabelle verwenden und komme mit der Datei nicht klar.
Wünschenswert wäre auch das jede Klasse eine eigene Datei hätte und es eine Datei für die Ausgabe der Extension gäbe.
Ich benutze die Version 3.0
thx!
Fieldset
Hi.
This extension really rocks.
I have a question. How to embed the detail into a fieldset?
Regards!
Formatting Radio Buttons..
I have radio button in the widget, but I would like them displayed horizontally.
I tried 'display'=>'inline', but it didn't work:
'ratingid'=>array( 'type'=>'radiolist', 'items'=>PerfReview::model()->getRatingOptions(), //'prompt'=>'Please Select Rating:', 'display'=>'inline' **//This doesn't work** ),
Any Suggestions?
formbuilder
This is an issue of how to create forms with the formbuilder.
Please post it in the forum, or in the multimodelform topic
In this case, I would first work with the form builder to generate a form you like and afterwards integrate this detailform in the multimodelform.
Thanks for the forum topic link.
Didn't know there was one..
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 ...
how looping multimodelform
How create Controller and view for online test using multimodelform.
Please help me
Great !
Just loved it! Many Thanks.
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
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.
Dependent dropdown
Thanks very much for your prompt reply. Will work on it and let you know if I succeed :)
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
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!!
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
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!
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.
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.
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
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.
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(); }"; }
UPD: if you want to use key=>value pairs, you shoud use my fork of original combobox: https://github.com/uniconstructor/yii-combobox
Attention: my fork may has no backwards compatibility with original combobox plugin.
Anyway, if you want to use complex select elements you better use the Joze Senegacnik's code for select2: http://www.yiiframework.com/extension/multimodelform#c13011
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
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.
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.
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); }
how to create inline items as asked by kunbolat and how to render the add item button to the bottom of form?
I have tried this comment above. but no luck for me. i have add the add item button to the bottom. But i am very much new to yii and the MultiModelForm looks complex for my understanding.
And also i have same requirement to add items as asked by kunbolat Inline form items and a link to remove that but i am unable to achieve it with out some experts help...
I would be grateful if anybody can help me out in this..
Thanks
Minimum entrys
Is it possible to require a minimum number of entries?
In the demo example (Group x Member) i would like to require a minimum of 3 members at each group, and it would be nice if the tree "member" could be created on init.
Check entries: an idea only - not tested
You can check the recordcount in the controller (onUpdate, onCreate...)
if (MultiModelForm::validate($member,$validatedMembers,$deleteItems)) { $minOk = count(validatedMembers)>=3; if(!$minOk) { ... set userflash error .... or add an error to the model .... } else MultiModelForm::save($model,$validatedItems,$deleteItems,$masterValues); }
Also creating three members you should do in the actionCreate.
$member = new Member; $validatedMembers = array(); //if not form submitted if(!isset($_POST['Group'])) { for($i=0;$i<3;$i++) { $validatedMembers[] = new Member; } }
Check minimum entries
@Joblo
Firstly, thanks for the tip, it was very helpful. In addition to your tips i wanted to give a message to the user when he clicks on the remove link and the minimum limit was reached, so I added to properties in MultiModelForm
public $minLimit = 0; public $minLimitText = 'Minimum records reached';
In function run i added the minLimit variable to the script, after the mmfRecordCount add
echo CHtml::script('mmfRecordCount=' . $this->_recordCount); echo CHtml::script('mmfMinLimit=' . $this->minLimit);
In function getRemoveLink I added some javascript code to the onClick event of the remove button to check the minimum limit.
if (!empty($this->removeConfirm)) $onClick = "if(confirm('{$this->removeConfirm}')) " . $onClick; $checkMinLimit = "if (mmfRecordCount <= mmfMinLimit) { alert('" . $this->minLimitText . "'); return false; } "; $onClick = $checkMinLimit . $onClick;
And thats it
Please add link to official github repository
Hello, Joblo.
I want to make a pull request to your project, but I can't find a link to your official repository.
I found this: https://github.com/yii-joblo/multimodelform
Am I correct? Is it your project?
Regards, Ilya.
github
Yes, this is the correct github link: https://github.com/yii-joblo/multimodelform
CJuiAutoComplete does not work well
CJuiAutoComplete + multimodelform:
It works fine for the first row, but the second does not show autocomplete support
What's wrong?
this is my _form.php
<?php /* @var $this PedidoController */ /* @var $model Pedido */ /* @var $form CActiveForm */ ?> <div class="form wide"> <?php $form=$this->beginWidget('CActiveForm', array( 'id'=>'pedido-form', 'enableAjaxValidation'=>false, )); ?> <p class="note">Los campos marcados con <span class="required">*</span> son obligatorios.</p> <?php echo $form->errorSummary(array_merge(array($model),$validatedDetalle)); ?> <div class="row"> <?php echo $form->labelEx($model,'numeroPedido'); ?> <?php echo $form->textField($model,'numeroPedido',array('disabled' => true,'style'=>'width:80px;color:green;')); ?> <?php echo $form->error($model,'numeroPedido'); ?> </div> <div class="row"> <?php $detalleFormConfig = array( 'elements'=>array( 'descripcion'=>array( 'type'=>'zii.widgets.jui.CJuiAutoComplete', 'name'=>'descripcion', 'value'=>$detalle->descripcion, 'visible' => true, 'source'=>$this->createUrl('autocompletedescripcion'), 'options'=>array( 'showAnim'=>'fold', ), ), 'precio'=>array( 'type'=>'text', 'size'=>'10', 'maxlength'=>10, 'id'=>'precio', 'STYLE'=>'text-align:right; width:60px', ), )); $this->widget('ext.multimodelform.MultiModelForm',array( 'id' => 'id_detalle', //the unique widget id 'formConfig' => $detalleFormConfig, //the form configuration array 'model' => $detalle, //instance of the form model 'validatedItems' => $validatedDetalle, 'data' => empty($validatedItems) ? $detalle->findAll('pedido_id=:idPedido', array(':idPedido'=>$model->id)): null, 'showAddItemOnError' => false, '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' => 'Agregar', 'removeText' => 'Eliminar', 'removeConfirm' => '¿ Eliminar el artículo seleccionado ?', )); ?> </div> <div class="row buttons"> <?php echo CHtml::submitButton($model->isNewRecord ? 'Guardar' : 'Actualizar'); ?> </div> <?php $this->endWidget(); ?> </div><!-- form -->
My MultiModelForm.php ...
/** * 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); }"; }
Thanks so much !!!
Re: CJuiAutoComplete does not work well
Hello, fart :)
This error happens because you are forgot to set 'jsAfterNewId' in your multimodelform widget.
Add string like that into your widget configuration array inside "_form.php", and everything will work fine.
'jsAfterNewId' => MultiModelForm::afterNewIdAutoComplete( $detalleFormConfig['elements']['descripcion']),
Regards, Ilya.
Re: CJuiAutoComplete does not work well
Hello Ilya,
Thank you very, very. very much .... It works perfect now !!!!!!!!
After deleting a row
Hi,
please.... How can I execute javascript code after deleting a row ?
Thanks a lot
js on deleting rows
The 'onclick' event of the removeLink is generated by the extension,
but the removeHtmlOptions used as htmloptions for the remove-link is a public property.
Maybe you can execute js-code after deleting by setting the 'onmouseup' in the removeHtmlOptions.
Deleting a row
Thanks JoBlo.
I tried
'removeHtmlOptions' => array ('onMouseUp' => 'updateField ()')
but the event onMouseDown onMouseUp and onclick run before, which I can not update the field to delete the row.
I have another problem. When I delete a row that is not the last, I duplicated the "id" of all fields of the last row. It could prevent the counter ids of the flas fields are decremented to delete a row? Use the id to update another field in the row in the onChange event of one of them.
Deleting a row
Hello,
Can I restrict the deletion to the last row inserted ? Only the last one.
removeLink
Your wishes are very specific, I don't know how to implement this generally.
The best is, you create your own MultiModelForm widget, that inherits MultiModelForm and override the methods you need. Search for the getRemoveLink method in your code to see where/when the removelink is rendered.
Update a field
All this I ask is because I try to update a field in the same row (by js) once enter a value in another and I do not know how to do if the Id of the fields are duplicated from one row to another when you delete a row except the last.
Any suggestions?
Anyway, thank you very much JoBlo, for this great extension
Many members
Hello All. I have the database
==== Class Group ==== id name ==== Member1 ==== id group_id Teacher ==== Member2 ==== id group_id Student ==== Member3 ==== id group_id Course
My Question is how to create more than 1 member instance.
for example :
public function actionCreate() { $model=new Group; $member1=new Member1; $member2=new Member2; $member3=new Member3; $member4=new Member4; ....//and other more than 10 members
Getting ID number of cloned elements.
Hi, any idea how I can get id number of cloned elements? Thank you!
Id of cloned elements
You can register a js-function 'jsAfterCloneCallback' since v5.0.
See the example for the property jsAfterCloneCallback in the documentation above.
ID number of cloned elements.
Hi Joblo. Thanks. But I would like to specifically get the ID number for a specific element. For example, if I have one field that which its id called Text_field, I would to get the number of the ID after it has been cloned. e.g.:
problem with delete.
first of all thank to your work. i have one problem when delete group , it will provide problem for me.
Validation near fields
If instead of using:
echo $form->errorSummary(array_merge(array($model),$validatedMembers));
We wish to display the error messages near each field on the form.
Is this possible to accomplish?
Lets say we have a form with Team and Members, and for each member, that a user can add or remove via ajax, if they are filled in and present, we wish to validate them and display the error near those form fields.
Is this possible?
Thanks.
display errors
An input error (generated by the model rules) should be displayed near the fields too, not only in errorSummary.
Did you check the demo, leaving lastname of the member blank (is required in model rules)?
FYI
First of all great extension. It works great and saved me tons of time.
In action update / create with a drop down it kept inserting a new row into my database even though my items were not set to add them in the form on my dropdown. i had to change your example for action update/ create to check if my field had an input.
public function actionUpdate($id) { Yii::import('ext.multimodelform.MultiModelForm'); $model=$this->loadModel($id); //the Group model $physician = new PatientsPhysician; $validatedPatientPhysician = array(); //ensure an empty array if(isset($_POST['Patients'])) { $model->attributes=$_POST['Patients']; //the value for the foreign key 'physician_id' $masterValues = array ('patient_id'=>$model->id); /* check to see if field is set on dropdown*/ if(isset($_POST['PatientsPhysician']['physician_id'])) { if( //Save the master model after saving valid physicians MultiModelForm::save($physician,$validatedPatientPhysician,$deleteMembers,$masterValues) && $model->save() ) $this->redirect(array('view','id'=>$model->id)); } if($model->save()){ $this->redirect(array('view','id'=>$model->id)); } } $this->render('update',array( 'model'=>$model, //submit the physician and validatedItems to the widget in the edit form 'physician'=>$physician, 'validatedMembers' => $validatedPatientPhysician, )); }
and my view is as follows
<?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 $paitentFormConfig = array( 'elements'=>array( 'physician_id'=>array( 'type'=>'dropdownlist', 'empty'=>' ', //it is important to add an empty item because of new records 'items'=>CHtml::listData( Physician::model()->findAll(), 'id', 'first_name'), )), ); $this->widget('ext.multimodelform.MultiModelForm',array( 'id' => 'id_physicians', //the unique widget id 'formConfig' => $paitentFormConfig, //the form configuration array 'model' => $physician, //instance of the form model //if submitted not empty from the controller, //the form will be rendered with validation errors 'validatedItems' => $validatedMembers, 'tableView' => false, //sortable will not work 'fieldsetWrapper' => array('tag' => 'div', 'htmlOptions' => array('class' => 'col-md-3')), 'removeLinkWrapper' => array('tag' => 'div', 'htmlOptions' => array('class'=>'red', 'style'=>' color: ffffff;')), 'removeHtmlOptions'=>array('class'=>'portfolio-btn', 'style'=>' color: ffffff;'), 'addItemText' => 'Add Physician', 'removeText' => 'Remove Physician', 'removeConfirm' => 'Are you sure you want to remove this physician?', //array of member instances loaded from d 'data' => $physician->findAll('patient_id=:patientId', array(':patientId'=>$model->id)), )); ?>
Template?
Is there anyway to use a template for the fields? I want to wrap them inside multiple divs and span classes. i.e. like this
<div class="col-md-4"> <div class="input-icon"> <label class="control-label"><?php echo $form->labelEx($model,'state'); ?></label> <i class="fa fa-home"></i> <?php echo $form->dropDownList($model, 'state', CHtml::listData( States::model()->findAll(), 'id', 'state'),array('class'=>'form-control', 'prompt' => 'Select a State','maxlength'=>2, 'style'=>'padding-left:30px;')); ?> </div> <span class="help-block"> <?php echo $form->error($model,'state'); ?> </span> </div>
Also how do you get the id for the item? i want to get it to show related data on the form.
3 member in one Group
is it possible to build 3 member in one form??
Initial setup option
Thanks for the extension! Is there any chance to setup initial number of rows in MMF? It starts with no child rows, but in general case there always should be at least 1.
One row visible
You have to set the attribute 'hideCopyTemplate'=>false, default is true.
This will always add an empty row = the row that will be cloned by js.
$this->widget('ext.multimodelform.MultiModelForm',array( ... 'hideCopyTemplate' => false, ... ));
Thanx!
Thanx Joblo for the hint!
I had 5.0 version and it duplicated "remove" link (in bootstrap, table mode) too many times each line you add. Then I've updated to 6.0 and GREAT- you fixed this issue! ;) Thanx again!
[SOLVED] One more problem...
Sorry for asking, but it seems to me that there's little bug in extension.
When I use 1 JuiAutocomplete in a row - everything is ok.
But when I add one more JuiAutocomplete (for another field in the same child table) something goes wrong after I begin to ADD ROWS. Both different autocompletes in first row search perfectly (and separately). But in all added AFTER first row lines both autocompletes are searching the same source url - looks like it copies only 1st autocomplete (they look separate but the functioning is like it's the same instance).
$memberFormConfig = array( 'elements'=>array( 'invent_num_and_title'=>array( 'type'=>'zii.widgets.jui.CJuiAutoComplete', 'source'=>Yii::app()->createUrl('/inventory/inventcard/searchInventCard'), 'options'=>array( 'showAnim'=>'fold', 'select' =>'js: function(event, ui) { this.value = ui.item.value; return false; }', ), ), 'department_to'=>array( 'type'=>'zii.widgets.jui.CJuiAutoComplete', 'source'=>Yii::app()->createUrl('/personnel/department/searchDepartment'), 'options'=>array( 'showAnim'=>'fold', 'select' =>'js: function(event, ui) { this.value = ui.item.value; return false; }', ), ), ));
and the way I add them to _form:
'jsAfterNewId'=>MultiModelForm::afterNewIdAutoComplete($memberFormConfig['elements']['invent_num_and_title']) . MultiModelForm::afterNewIdAutoComplete($memberFormConfig['elements']['department_to']),
Can please try to use 2 seperate autocompletes (try >1 rows copied) to check is this is an issue? Or tell me where's my fault...
[UPDATE]
YES! I'm newbie in JS so it took me more than 3 hours to fix this issue. One of the possible problem with my approach - is that 'afterNewIdAutoComplete' function somehow double-overwrite properties each time I add new row. So I decided to check only 'proper' objects (to put some filter to afterNewIdAutoComplete function).
In order to do that 1) I've added new property to JUIAutocomlete config 'common_id_string' as a common string part of all id's of that particular field (generally - I duplicate element's name):
... 'invent_num_and_title'=>array( 'type'=>'zii.widgets.jui.CJuiAutoComplete', 'source'=>Yii::app()->createUrl('/inventory/inventcard/searchInventCard'), 'options'=>array( 'showAnim'=>'fold', 'common_id_string'=>'invent_num_and_title', // I declared it once more to have access to it in JS function ... // did the same but with with 'common_id_string'=>'department_to'
2) then I've changed one line in afterNewIdAutoComplete function (MultiModelForm.php:419)
return "if ( this.hasClass('ui-autocomplete-input') && this[0].id.indexOf('{$element['options']['common_id_string']}')>=0 )
And it worked perfectly!
If there's more elegant way to do it, please inform me. Maybe it would be very helpful to integrate some type of this problem's solution in future release of MMF.
Thank you!
Including row numbers?
This is my first extension to implement while learning YII and I must say I can't see developing without it. It's also pretty easy to follow. But, how can I have the row number populate a text field value? I can find the mmfRecordCount variable in the .js and MultiModelForm.php files but my newbie skills lack the ability to access it when clicking the 'Add Item'.
For example:
$hhmembersFormConfig = array( 'elements'=>array( 'hh_member_no'=>array( 'type'=>'text', 'maxlength'=>40, 'disabled'=>true, 'value'=>$row, // or item number count ),
When I click on 'Add Item' I would like the household member number (hh_member_no) to be 1,2,3,etc.
Thanks!
Incompatible with Chosen
I have problem when implement it with chosen, probably adding this feature will be great?
Dependent Drop Down
Shall I use dependent drop down in following coding
$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,), ), ));
Please reply and guide me.... I am new in Yii
number of row
Hi Jobbo and all members,
I wanna ask you how to set the number of row in MMF ?
If I set
'hideCopyTemplate' => false,
It will be generated 1 row, I want to make it 5 or 10.
How to make it possible ?
The second one, if I click add item, the row also will be added 1. how to make it add 5 or 10 all at once ?
Thank you in advance.
Can you help me ?
Thanks
Tome
If you have any questions, please ask in the forum instead.
Signup or Login in order to comment.