Django-Dynamic-Formset
by areski
HTML
<form id="demo">
<input name="prefix-INITIAL_FORM_COUNT" value="3" type="hidden">
<input name="prefix-TOTAL_FORM_COUNT" value="3" type="hidden">
<fieldset disabled class="empty-form" style="display: none">
<fieldset>
<label for="id_prefix-__prefix__-name">Name</label>
<input type="text" name="prefix-__prefix__-name" value="initial" id="id_prefix-__prefix__-name">
<button type="button" data-formset-remove-form>
DELETE
</button>
</fieldset>
</fieldset>
<fieldset class="forms">
<fieldset>
<label for="id_prefix-0-name">Name</label>
<input type="text" name="prefix-0-name" id="id_prefix-0-name" value="Alice">
</fieldset>
<fieldset>
<label for="id_prefix-1-name">Name</label>
<input type="text" name="prefix-1-name" id="id_prefix-1-name" value="Bob">
</fieldset>
<fieldset>
<label for="id_prefix-2-name">Name</label>
<input type="text" name="prefix-2-name" id="id_prefix-2-name" value="Carol">
</fieldset>
</fieldset>
<fieldset class="controls">
<button type="button" data-formset-add-form>
Add new form
</button>
</fieldset>
</form>
<!-- Add some space here, so that the buttons don't get hidden -->
<!-- when we add new rows when embedded -->
<br>
<br>
<br>
<br>
CSS
fieldset {
border: none;
}
JavaScript
function Formset(element) {
/*
Dynamic Formset handler for Django formsets.
Events:
* init.formset
* add-form.formset
* remove-form.formset
* renumber-form.formset
*/
if (!(this instanceof Formset)) {
return new Formset(element);
}
var formset = this;
var emptyForm = element.querySelector('.empty-form').firstElementChild;
var formsList = element.querySelector('.forms');
var initialForms = element.querySelector('[name$=INITIAL_FORM_COUNT]');
var totalForms = element.querySelector('[name$=TOTAL_FORM_COUNT]');
var prefix = initialForms.name.replace(/INITIAL_FORM_COUNT$/, '');
//alert(prefix);
function addForm(event) {
// Duplicate empty form.
var newForm = emptyForm.cloneNode(true);
// Update all references to __prefix__ in the elements names.
renumberForm(newForm, '__prefix__', totalForms.value);
// Make it able to delete itself.
newForm.querySelector('[data-formset-remove-form]').addEventListener('click', removeForm);
// Append the new form to the formsList.
formsList.insertAdjacentElement('beforeend', newForm);
element.dispatchEvent(new CustomEvent('add-form.formset', {
detail: {
form: newForm,
formset: formset
}
}));
// Update the totalForms.value
totalForms.value = Number(totalForms.value) + 1;
}
function getForm(target) {
var parent = target.parentElement;
if (parent == document) {
return null;
}
if (parent == formsList) {
return target;
}
return getForm(parent);
}
function renumberForm(form, oldValue, newValue) {
var matchValue = prefix + oldValue.toString()
var match = new RegExp(matchValue);
var replace = prefix + newValue.toString();
['name', 'id', 'for'].forEach(function(attr) {
form.querySelectorAll('[' + attr + '*=' + matchValue + ']').forEach(function(el) {
el.setAttribute(attr, el.getAttribute(attr).replace(match, replace));
});
...