nested-formset
nested-formset copied to clipboard
How to make it dynamic ?
I'm struggling with making it dynamic, like with jquery formsets, but that plugin wont work out of the box, how can I do it then ? thank you
Hi, this is the answer for someone like me who decided to make it dynamic.
I've faced this issue when it was required for me to build a "Bar chart builder" so that a user could place multiple charts with customizable rows on the page along with other ordinary formsets.
I've been using underscore.js since the first time I faced dynamic formsets and I came up with generic function which will be described below along with HTML code.
HTML:
{{ chart_formset.management_form }}
<div id="chart_container">
{% for chart in chart_formset %}
<div class="formsetForm">
{{ chart|crispy }}
{{ chart.nested.management_form }}
<div id="chart_row_container_{{ forloop.counter0 }}">
{% for chart_row in chart.nested %}
<div class="formsetForm">
{{ chart_row|crispy }}
</div>
{% endfor %}
</div>
<!-- data-counter is required for our function to know to which container it should append a form —>
<input type="button" data-formset="chart_row" data-counter="{{ forloop.counter0 }}"
value="Add Chart Row" class="btn btn-success add-formset"/>
</div>
{% endfor %}
</div>
<input type="button" data-formset="chart" value="Add Chart" class="btn btn-success add-formset"/>
JS:
{{ block.super }} // jQuery, etc.
{{ chart_formset.empty_form.nested.empty_form.media }}
<script src="{% static 'lib/underscore-min.js' %}"></script>
<script type="text/html" id="chart-template">
<div class="formsetForm">
{{ chart_formset.empty_form|crispy }}
// we need to add a management form for each nested formset
{{ chart_formset.empty_form.nested.management_form }}
<div id="chart_row_container___prefix__"></div>
<input type="button" data-formset="chart_row" data-counter="__prefix__" value="Add Chart Row" class="btn btn-success add-formset"/>
</div>
</script>
<script>
addFormsetForm = function (ev, formset_name) {
ev.preventDefault();
var formset = $(this).data('formset') || formset_name,
formsetSplit = formset.split('_'),
counter = $(this).data('counter'),
$container = typeof counter != 'undefined' ? $('div#' + formset + '_container_' + counter) : $('div#' + formset + '_container'), // find the container
count = $container.children().length,
tmplMarkup = $('#' + formset + '-template').html(), // find the template
compiledTmpl;
// compile our template
if (typeof counter != 'undefined') {
compiledTmpl = tmplMarkup.replace(
new RegExp('__prefix__-' + formsetSplit[1] + 's-__prefix__', 'g'),
counter + '-' + formsetSplit[1] + 's-' + count
).replace(/__num__/g, count + 1);
}
else
compiledTmpl = tmplMarkup.replace(/__prefix__/g, count).replace(/__num__/g, count + 1);
$container.append(compiledTmpl);
// bind mouse click on newly created “Add formset” button
$container.find('.add-formset[data-counter=' + count + ']').click(addFormsetForm);
// update form count
var id;
if (typeof counter != 'undefined')
id = formsetSplit[0] + 's-' + counter + '-' + formsetSplit[1] + 's'
else
id = formset + 's';
$('#id_' + id + '-TOTAL_FORMS').attr('value', count + 1);
};
$('.add-formset').click(addFormsetForm);
</script>