This extension is for usage in forms to copy or clone input elements or other DOM elements.
This is a wrapper for the jQuery plugin: relCopy
It's similar to the extension jappendo but easier to use and more flexible. Every clone can be removed by adding a remove link or icon.
Notes ¶
Cloning datepicker is available since v1.1. Don't forget to delete the assets folder of the widget when upgrading to the new version.
See multimodelform if you need this functionality for clientside cloning records with batchUpdate/Insert/Delete at the server side. This extension will do the workaround discussed below for you.
Usage ¶
- Extract the files under .../protected/extensions
- Add the widget code somewhere in the view
The simplest usage:
$this->widget('ext.jqrelcopy.JQRelcopy',
array(
'id' => 'copylink',
//'removeText' => 'remove' //uncomment to add remove link
);
All options:
$this->widget('ext.jqrelcopy.JQRelcopy',array(
//the id of the 'Copy' link in the view, see below.
'id' => 'copylink',
//add a icon image tag instead of the text
//leave empty to disable removing
'removeText' => 'Remove',
//htmlOptions of the remove link
'removeHtmlOptions' => array('style'=>'color:red'),
//options of the plugin, see http://www.andresvidal.com/labs/relcopy.html
'options' => array(
//A class to attach to each copy
'copyClass'=>'newcopy',
// The number of allowed copies. Default: 0 is unlimited
'limit'=>5,
//Option to clear each copies text input fields or textarea
'clearInputs'=>true,
//A jQuery selector used to exclude an element and its children
'excludeSelector'=>'.skipcopy',
//Additional HTML to attach at the end of each copy.
'append'=>CHtml::tag('span',array('class'=>'hint'),'You can remove this line'),
)
))
- Add a 'Copy' link with the id, defined for the widget above. You have to add the attribute 'ref'. The value is a jquery-selector to define the element to copy. The code below will copy the div with the class 'copy'.
<a id="copylink" href="#" rel=".copy">Copy</a>
<div class="row copy">
<?php echo CHtml::label('Title',''); ?>
<?php echo CHtml::textField('title[]','Title'); ?>
<?php echo CHtml::textField('fld',"Don't copy",array('class'=>'skipcopy')); ?>
</div>
Usage with date/time picker ¶
Widgets needs a javascript workaround on cloning. We have to assign the CJuiDatePicker functionality to the cloned new element.
Since v1.1 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.
Save the config of the in a variable in the view.
$datePickerConfig = array('name'=>'dayofbirth',
'language'=>'de',
'options'=>array(
'showAnim'=>'fold',
));
$this->widget('zii.widgets.jui.CJuiDatePicker',$datePickerConfig);
You have to assign the javascript code to the property 'jsAfterNewId'.
$this->widget('ext.jqrelcopy.JQRelcopy',
array(
'id' => 'copylink',
'removeText' => 'remove',
//'jsBeforeNewId' => "alert(this.attr('id'));",
//add the datapicker functionality to the cloned datepicker with the same options
'jsAfterNewId' => JQRelcopy::afterNewIdDatePicker($datePickerConfig),
));
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 JQRelcopy.php).
Resources ¶
Projectpage of the jQuery plugin relCopy
Changelog ¶
- v1.1 Added support for cloning date/time widgets
- v1.1.1 chrome issue: removeAttr, see comment from 'jackfiallos' below
More Explanation on adding to the DB
Can you please explain how you can save it to the DB. Since it is an array i have hard to to analyze it. Can you also explain the model and controller structure. I know this is too much to ask, But an example would be GREAT. I have also tried Jappendo, but this is very light!
Short explanation
Only some code snippets, not tested.
Assume you have a model 'Person' defined as ActiveRecord (id,firstname,lastname) and you want to edit all persons in one single form. You have the model / CRUD controller / views generated with gii.
The actionUpdate of the controller:
protected function savePersons($formData) { if (empty($formData) return; $result = array(); $idx=0; //You will get 3 arrays in $formData: id, firstname, lastname foreach($formData['firstname'] as $firstName) { $model = new Person; $model->firstname = $firstName; //The other attributes can be found at the same postion in the formData $model->lastname = $formData['lastname'][$idx]; //no id is submitted for new items if(!empty($formData['id'][$idx]) $model->id= $formData['id'][$idx]; if(!$model->save()) return false; $idx++; } return true; } public function actionUpdate() { if(isset($_POST['Person'])) { if(this->savePersons($_POST['Person'])) $this->redirect(array('index')); } $model = new Person(); $data = $model->findAll(); // get all at once $this->render('update',array( 'data'=>$data, )); }
In the 'update' view do
... $this->renderPartial('_form',array('data'=>$data)); ...
In the '_form' you can't use the default $form->active ... stuff, because you have to name the attributes as array with added '[]': 'Person[firstname][]'
... <?php $removeText = 'remove'; $this->widget('ext.jqrelcopy.JQRelcopy',array( 'id' => 'add_persons', 'removeText' => $removeText, 'options' => array('excludeSelector'=>'.nocopy'), )); ?> .... <a id="add_persons" href="#" rel=".copy">Add person</a> <?php if (empty($data)): ?> <div class="row copy"> <?php echo CHtml::encode('Firstname'); ?> <?php echo CHtml::textField('Person[firstname][]',''); ?> <?php echo CHtml::encode('Lastname'); ?> <?php echo CHtml::textField('Person[lastname][]',''); ?> </div> <?php else: ?> <?php $idx = 0; $count = count($data); foreach($data as $person): //the last added row is the row to copy $copyClass = ($idx == $count-1) ? ' copy' : ''; ?> <div class="row<?php echo $copyClass; ?>"> <?php //don't allow to edit the id, don't copy the id to new records echo CHtml::hiddenField('Person[id][]',$person->id,array('class'=>'nocopy')); echo CHtml::tag('span',array('class'=>'nocopy'),$person->id); ?> <?php echo CHtml::encode('Firstname'); ?> <?php echo CHtml::textField('Person[firstname][]',$person->firstname); ?> <?php echo CHtml::encode('Lastname'); ?> <?php echo CHtml::textField('Person[lastname][]',$person->lastname); //Next: Add a remove link to existing records too. ?> <a class="nocopy" onclick="$(this).parent().remove(); return false;" href="#"><?php echo $removeText; ?></a> </div> <?php $idx++; endforeach; ?> <?php endif; ?> .... ...
NOTE: This is unfinished. You have to do a workaround when existing records where removed.
Don't add the remove link to existing records, add a checkbox instead (with id value) to remove on saving (maybe the best way).
Delete all records from the table before saving??
Handling with hidden fields to determine which records to delete??
Hope that helps.
See tabular form too
Another solution for working with jqrelcopy and multiple input fields might be 'tabular input':
See the documentation Collecting Tabular Input
CJuiAutoComplete cloning issue
How to clone a CJuiAutoComplete widget using this extension ? Any suggestions ?
CJuiAutoComplete cloning issue
How to clone a CJuiAutoComplete widget by using this extension ? Any suggestions ?
Cloning autocomplete impossible
Sorry, it's impossible to clone a CJuiAutoComplete.
The problem is the JQuery script in the jQuery's ready function.
jQuery('#autocomplete_name').autocomplete(...)
This part is not cloneable for the RelCopy plugin.
I can't see a way to produce
jQuery('#autocomplete_name2').autocomplete(...); jQuery('#autocomplete_name3').autocomplete(...); ....
when the autocomplete input is cloned.
getting datepicker to copy
To allow datepickers to be copied, the JS file needs this line in the main function (after « var clone .... ») :
~~~
[javascript]
// reset datepicker
$(clone).find(':input').removeClass("hasDatepicker");
~~~
Cloning datepicker
ianare: Many thanks for your hint.
Now I was able to make cloning datepicker working in 'jqrelcopy' and 'multimodelform'
Maybe you have an idea about CJuiAutoComplete. See the note above.
jui
I'm in the process of merging your improvements with my modified version of the extension.
It was simpler for me to implement the timepicker support because I also modified the extension to use a jquery delegate() binding, rather than having to recreate on each clone. Downside is that all the timepickers on the page will have the same settings ... but this is fine for my needs, I do need to be able to clone multiple timepickers at the same time.
I now have to get the autocomplete and comboboxes to work with the extension so if I find anything I'll keep you posted.
Merging
Good news that you are still working on jqrelcopy.
I'm looking forward to merge your code with the current existing.
using delegate
Me again ;-) I came across a problem when the copy button itself is manipulated by DOM modifications. Replacing the assignment
this.each(function() { $(this).click(function){ ... CLICK FUNCTION ... } );
with a delegate did the trick :
$('body').delegate("#"+$(this).attr('id'),"click", function(){ ... CLICK FUNCTION ... );
now on git hub
I've put up my modifications to the extension up on github, for those interested.
https://github.com/digitick/yii-jqrelcopy
Cloning CJuiAutoComplete
Friends,
Did anyone manage to clone CJuiAutoComplete by using this extension ?
unique id to cloned fields (dropdown box, text box)
Hi Guys,
Did anyone know how to assign new unique id's to cloned fields ? Appreciate your suggestions.
A Bug to fix in jquery.relcopy.yii.1.0.js
Chrome has a little bug with checkbox fields .. so simply correct the 114 line
from
~~~
[javascript]
$(this).removeAttr('checked', '');
~~~
to
[javascript] $(this).removeAttr('checked');
no such file or directory
include(JQRelcopy.php) [function.include]: failed to open stream: No such file or directory
I got this problem when i try to put this code in my widget
'jsAfterNewId' => JQRelcopy::afterNewIdDatePicker($datePickerConfig),
but my jqrelcopy works for my datepicker.
the problem is when i click on the duplicated datepicker, the original one is being edited.
import the class
This Yii errormessage tells, that the class 'JQRelcopy' cannot be found.
You have to Yii::import('ext.jqrelcopy.JQRelcopy') in your view.
You need the afterNewIdDatePicker code, to make the duplicated datetimepicker edit, not the original.
Next time please comment in the forum about issues.
Problem with fileField
I have been trying to solve it but :(
can you answer http://www.yiiframework.com/forum/index.php?/topic/28067-chtmlfilefield-with-jqrelcopy/
Clone date
hello!! I try to use this extension that is good!! but I still have problem with datetimepicker I can't Clone date it just show me only the first row of form but the second row it not show Date calendar like the the first. Plz Explan more about this!! sorry I'm not good english for writting!! :)
My Patched up JQrelcopy
Please see this Forum Post.
This enables JQrelcopy to create a uniqueID for each name attribute whether on update or create. It uses a regex to replace the contents of '[]' in model input names.
Example: input name="Model[$index][field]" id="Model_$index_field"
Therefore, when cloning a row of data you will get back an indexed array with each field of the model as an array key.
Example
Model:
0
field1
field2
field3
Credit to Ianare for the idea (I wasn't able to get his modified version working for my needs).
An improvement
Hi to all, i've modified this great extension making an improvement. Now it's possible to insert the remove text inside an html tag.
All was born for my need to insert the remove text inside a td tag of a table.
Istruction to apply the modify:
1) Add the following public member in the head of file JQRelcopy.php:
public $removeTextContainer = array();
2) modify the following code in the init function:
ORIGINAL:
if (!empty($this->removeText)) { $onClick = '$(this).parent().remove(); return false;'; $htmlOptions = array_merge($this->removeHtmlOptions,array('onclick'=>$onClick)); $append = CHtml::link($this->removeText,'#',$htmlOptions); $this->options['append'] = empty($this->options['append']) ? $append : $append .' '.$this->options['append']; }
Replace with:
if (!empty($this->removeText)) { $onClick = '$(this).parent().remove(); return false;'; $htmlOptions = array_merge($this->removeHtmlOptions,array('onclick'=>$onClick)); $append = ""; if(!empty($this->removeTextContainer)){ $htOpt = $this->removeTextContainer[1]==null ? array() : $this->removeTextContainer[1]; $closeTag = $this->removeTextContainer[2]==null ? false : $this->removeTextContainer[2]; $onClick = '$(this).parent().parent().remove(); return false;'; $htmlOptions = array_merge($this->removeHtmlOptions,array('onclick'=>$onClick)); $append .= CHtml::tag($this->removeTextContainer[0],$htOpt,CHtml::link($this->removeText,'#',$htmlOptions),$closeTag); } else{ $append .= CHtml::link($this->removeText,'#',$htmlOptions); } $this->options['append'] = empty($this->options['append']) ? $append : $append .' '.$this->options['append']; }
3) in the config array of the widget, if you want to use this new option, declare the following attribute
//option to insert the remove text in a container insert by CHtml::tag //first parameter is the tag parameter of CHtml::tag //second parameter is the htmloption array of CHtmtl::tag //third paramater is the CloseTag parameter of CHtml::tag //the third parameter of CHtml::tag is not mapped because //were filled with the CHtml::link tag of Remove Button 'removeTextContainer'=>array("td",null,false),
Thank to all
Regards,
Seniorboss
Probleme CJuiDatePicker in JQRelcopy
I have a problem with CJuiDatePicker in the module JQRelcopy.
I use this module as I have a form with a datepicker field to be copied as many times as the user wishes.
The module works fine if I put a normal field (without datepicker), but as soon as I add the module CJuiDatePicker which is also a module of the Yii framework, I have a blank page appears.
I study the problem and I saw that it was when I Atoute the following line in the properties of my widget JQRelcopy the problem occurs:
'jsAfterNewId' => JQRelcopy::afterNewIdDatePicker($datePickerConfig),
in
$this->widget('ext.jqrelcopy.JQRelcopy', array( 'id' => 'copylink', 'removeText' => 'remove', 'jsAfterNewId' => JQRelcopy::afterNewIdDatePicker($datePickerConfig), ));
I look in several forums and anywhere a person has had the same problem as me.
Can you help me?
Sorry, for my English
how to apply client side validation to clone elements?
thanks for extension but i have a question
how to apply client side validation to clone elements?
thanks in advance
clientside validation
If you are working with models, you should better use the
extension multimodelform
You can validate the models with the rules() of the model.
If you have any questions, please ask in the forum instead.
Signup or Login in order to comment.