Last active
August 29, 2015 13:59
-
-
Save juanmf/10483041 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Recursive Prototype Clonner: | |
* it relies on the following data structure: | |
* <someAncestor data-allow-add="true"> ... | |
* <button class="" data-add="add" | |
* data-add-prototype-node="JQ selector relative to $button.closest('[data-allow-add]'). Will be parameter of $obj.find($selector)" | |
* data-add-prototype-name="<?php echo $prototypeName; ?> optional, defauts to __name__" | |
* data-add-append-node="%data-add-prototype-node% | JQ selector relative to $button.closest('[data-allow-add]'). | |
* Will be parameter of $obj.find($selector)" | |
* data-add-wrapper="html to wrap the cloned item before appending e.g. <li>__PROTOTYPE-CONTENT__</li>" | |
* > ... | |
* </someAncestor> | |
* e.g. | |
* <button class="" data-add="add" | |
data-add-prototype-node="ul.preciosTierra" | |
data-add-append-node="ul.preciosTierra" | |
data-add-wrapper="<?php echo htmlentities( | |
'<li data-allow-add="true">' | |
. $botonesDepto | |
. '__PROTOTYPE-CONTENT__' | |
. '</li>'); ?>" | |
>Crear Nueva Area</button> | |
*/ | |
/** from http://stackoverflow.com/questions/322912/jquery-to-find-all-previous-elements-that-match-an-expression */ | |
// this is a small helper extension i stole from | |
// http://www.texotela.co.uk/code/jquery/reverse/ | |
// it merely reverses the order of a jQuery set. | |
$.fn.reverse = function() { | |
return this.pushStack(this.get().reverse(), arguments); | |
}; | |
// create two new functions: prevALL and nextALL. they're very similar, hence this style. | |
$.each( ['prev', 'next'], function(unusedIndex, name) { | |
$.fn[ name + 'ALL' ] = function(matchExpr) { | |
// get all the elements in the body, including the body. | |
var $all = $('body').find('*').andSelf(); | |
// slice the $all object according to which way we're looking | |
$all = (name == 'prev') | |
? $all.slice(0, $all.index(this)).reverse() | |
: $all.slice($all.index(this) + 1) | |
; | |
// filter the matches if specified | |
if (matchExpr) $all = $all.filter(matchExpr); | |
return $all; | |
}; | |
}); | |
/***End from */ | |
docdigital = (typeof docdigital === "undefined") ? {} : docdigital; | |
$.extend(true, docdigital, { | |
constants: { | |
mediator: { | |
messages: { | |
clonePrototype_prototypeAdded: 'clonePrototype_prototypeAdded' | |
} | |
} | |
} | |
}); | |
$.extend(true, docdigital, { | |
ColonePrototype: function () { | |
this.bindDelete = function () { | |
$('[data-allow-add] button[data-delete]').attr('onclick','').unbind('click'); | |
$('[data-allow-add] button[data-delete]') | |
.click( | |
function(event) { | |
var $button = $(event.target); | |
var $container = $button.closest('[data-allow-add]'); | |
var $prototypeNode = $container.find(($button.attr('data-add-prototype-node'))); | |
var $appendTarget = $button.attr('data-add-append-node') === '%data-add-prototype-node%' | |
? $prototypeNode | |
: $container.find($button.attr('data-add-append-node')); | |
$appendTarget.each(function() { | |
var $toDelete = $(this).children().last(); | |
$toDelete.remove(); | |
}); | |
// child Counter update | |
parseInt($button.attr('data-count')) > 0 | |
&& $button.attr( | |
'data-count', | |
parseInt($button.attr('data-count')) - 1 | |
); | |
return false; | |
} | |
); | |
}; | |
this.bindAdd = function () { | |
$('[data-allow-add] button[data-add]').attr('onclick','').unbind('click'); | |
$('[data-allow-add] button[data-add]') | |
.click( | |
function(event) { | |
var $button = $(event.target); | |
var $container = $button.closest('[data-allow-add]'); | |
var $prototypeNode = $container.find(($button.attr('data-add-prototype-node'))); | |
var $prototypeName = $button.attr('data-add-prototype-name') || '__name__'; | |
var $appendTarget = $button.attr('data-add-append-node') === '%data-add-prototype-node%' | |
? $prototypeNode | |
: $container.find($button.attr('data-add-append-node')); | |
var $wrappingHtml = $button.attr('data-add-wrapper') && $button.attr('data-add-wrapper').length > 0 | |
? $button.attr('data-add-wrapper') | |
: false; | |
$button.attr('data-count') | |
|| $button.attr( | |
'data-count', | |
$appendTarget.first().children().length | |
); | |
var nameRegex = new RegExp($prototypeName, 'g'); | |
var labelRegex = new RegExp($prototypeName + 'label__', 'g'); | |
$appendTarget.each(function(idx, element) { | |
var $clone = $prototypeNode.eq(idx).attr('data-prototype'); | |
$clone = $clone.replace(labelRegex, $button.attr('data-count')); | |
$clone = $clone.replace(nameRegex, $button.attr('data-count')); | |
if ($wrappingHtml) { | |
$clone = $wrappingHtml.replace(/__PROTOTYPE-CONTENT__/g, $clone) | |
} | |
$clone = $.parseHTML($clone); | |
$(this).append($clone); | |
docdigital.mediatorInstance.send( | |
docdigital.constants.mediator.messages.clonePrototype_prototypeAdded, | |
$clone | |
); | |
}); | |
$button.attr('data-count', parseInt($button.attr('data-count')) + 1); | |
var c = new docdigital.ColonePrototype(); | |
$('li[data-allow-add] > div').each(c.tabIndxFn); | |
c.bindAdd(); | |
c.bindDelete(); | |
return false; | |
} | |
); | |
}; | |
} | |
}); | |
$(document).ready( | |
function () { | |
var clonner = new docdigital.ColonePrototype(); | |
clonner.bindAdd(); | |
clonner.bindDelete(); | |
} | |
); | |
this works with the following markup:
<script type="text/javascript" src="{{ asset('bundles/ddtools/js/clonePrototypes.js') }}"></script>
{#
* Renders a pair of buttons, add and delete.
* @param string class The class used by jQuery script to find relevant nodes.
* @param string title Printed <h3> Title </h3> for this buttons
* @param FormView form The rendering form
* @param string widgets The widget that is being rendered, should be a collectionType.
* @param string widgetBlock Optional. The block that knows the widget structure and renders it with custom markup.
*
#}
<div class="side_by_side" data-allow-add="true">
<h3>{{ title }}</h3>
<span class="buttons show control-group">
<button class="btn" data-add="add"
data-add-prototype-node="ul.{{ class }}"
data-add-append-node="ul.{{ class }}"
data-add-wrapper="{{ '<li class="clearfix panel panel-default container-fluid">__PROTOTYPE-CONTENT__</li>' | e }}"
>Crear Nuevo</button>
<button class="btn" data-delete="delete"
data-add-prototype-node="ul.{{ class }}"
data-add-append-node="ul.{{ class }}">Borrar Último</button>
</span>
<ul class="{{ class }}"
data-prototype="{{ form_widget(widgets.vars.prototype) | e }}">
{% for widget in widgets %}
<li class="clearfix panel panel-default container-fluid">
{% if widgetBlock is defined %}
{% include widgetBlock with { 'widget': widget } %}
{% else %}
{{ form_widget(widget) }}
{% endif %}
</li>
{% endfor %}
{% do widgets.setRendered %}
</ul>
</div>
Which gets included like this:
{% include 'DdToolsBundle:Common:allowAddWidget.html.twig'
with { 'widgets': edit_form.type, 'class': 'classForSelectors', 'title': 'Fields'} %}
Can you give an example of this code working with multiple nested collections?
Hello i have the same problem with multiple nested collections . can anybody help me
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
docdigital.mediatorInstance.send() is here https://gist.github.com/juanmf/10475007