Динамическое добавление формы в набор форм Django с помощью Ajax
Я хочу автоматически добавлять новые формы в набор форм Django с помощью Ajax, так что когда пользователь нажимает кнопку "Добавить", он запускает JavaScript, который добавляет новую форму (которая является частью набора форм) на страницу.
15 ответов:
вот как я это делаю, используя jQuery:
мой шаблон:
<h3>My Services</h3> {{ serviceFormset.management_form }} {% for form in serviceFormset.forms %} <div class='table'> <table class='no_error'> {{ form.as_table }} </table> </div> {% endfor %} <input type="button" value="Add More" id="add_more"> <script> $('#add_more').click(function() { cloneMore('div.table:last', 'service'); }); </script>в файле javascript:
function cloneMore(selector, type) { var newElement = $(selector).clone(true); var total = $('#id_' + type + '-TOTAL_FORMS').val(); newElement.find(':input').each(function() { var name = $(this).attr('name').replace('-' + (total-1) + '-','-' + total + '-'); var id = 'id_' + name; $(this).attr({'name': name, 'id': id}).val('').removeAttr('checked'); }); newElement.find('label').each(function() { var newFor = $(this).attr('for').replace('-' + (total-1) + '-','-' + total + '-'); $(this).attr('for', newFor); }); total++; $('#id_' + type + '-TOTAL_FORMS').val(total); $(selector).after(newElement); }что он делает:
cloneMoreпринимаетselectorкак первый аргумент, так иtypeиз formset как 2-й. Что заselectorдолжен сделать, это передать его, что он должен дублировать. В этом случае я передаю егоdiv.table:lastтак что jQuery ищет последнюю таблицу с классомtable. Элемент:lastчасть важно, потому чтоselectorтакже используется для определения того, что новая форма будет вставлена после. Скорее всего, вы захотите его в конце остальных форм. Элементtypeаргумент таков, что мы можем обновить наcloneMoreфункция смотрит на то, сколько форм в настоящее время есть, и проходит через каждый вход и метку внутри новой формы, заменяя все имена полей/идентификаторы из чего-то вродеid_clients-(N)-nameдоid_clients-(N+1)-nameи так далее. После его завершения он обновляетTOTAL_FORMSполе для отражения новой формы и добавления ее в конец набора.эта функция особенно полезна для меня, потому что способ ее настройки позволяет мне используйте его во всем приложении, когда я хочу предоставить больше форм в наборе форм, и мне не нужно иметь скрытую форму "шаблона" для дублирования, пока я передаю ей имя набора форм и формат, в котором формы выложены. Надеюсь, это поможет.
упрощенная версия ответа Паоло с помощью
empty_formкак шаблон.<h3>My Services</h3> {{ serviceFormset.management_form }} <div id="form_set"> {% for form in serviceFormset.forms %} <table class='no_error'> {{ form.as_table }} </table> {% endfor %} </div> <input type="button" value="Add More" id="add_more"> <div id="empty_form" style="display:none"> <table class='no_error'> {{ serviceFormset.empty_form.as_table }} </table> </div> <script> $('#add_more').click(function() { var form_idx = $('#id_form-TOTAL_FORMS').val(); $('#form_set').append($('#empty_form').html().replace(/__prefix__/g, form_idx)); $('#id_form-TOTAL_FORMS').val(parseInt(form_idx) + 1); }); </script>
Я размещен фрагмент из приложения, над которым я работал некоторое время назад. Похож на Паоло, но также позволяет удалять формы.
предложение Паоло прекрасно работает с одной оговоркой-кнопки браузера назад / вперед.
динамические элементы, созданные с помощью скрипта Паоло, не будут отображаться, если пользователь вернется к набору форм с помощью кнопки Назад/Вперед. Проблема, которая может быть нарушителем сделки для некоторых.
пример:
1) пользователь добавляет две новые формы в formset с помощью кнопки "Добавить-кнопку"
2) пользователь заполняет формы и отправляет formset
3) пользователь нажимает кнопку Назад в браузере
4) Formset теперь сводится к исходной форме, все динамически добавляемые формы не существуют
Это вовсе не дефект скрипта Паоло; но факт жизни с манипуляцией dom и кешем браузера.
Я полагаю, что можно хранить значения формы в сеансе и иметь некоторую магию ajax, когда набор форм загружается, чтобы снова создать элементы и перезагрузить значения из но в зависимости от того, как анальный вы хотите быть примерно одним и тем же пользователем и несколькими экземплярами формы это может стать очень сложным.
У кого есть хорошее предложение для борьбы с этим?
спасибо!
проверьте следующие решения для динамических форм django:
http://code.google.com/p/django-dynamic-formset/
https://github.com/javisantana/django-dinamyc-form/tree/master/frm
Они оба используют jQuery и являются специфичными для django. Первый кажется немного более полированным и предлагает загрузку, которая поставляется с демонстрациями, которые превосходны.
имитировать и подражать:
- создать набор форм, который соответствует ситуации до нажать кнопку "Добавить".
- загрузите страницу, просмотрите источник и обратите внимание на все
<input>поля.- измените набор форм в соответствии с ситуацией после нажав кнопку "Добавить" (изменить количество дополнительных полей).
- загрузите страницу, просмотрите источник и обратите внимание, как
<input>поля измененный.- создайте некоторый JavaScript, который изменяет DOM подходящим образом, чтобы переместить его из до state to the после государство.
- присоединить JavaScript к кнопке "Добавить".
хотя я знаю, что наборы форм используют специальные скрытые
<input>поля и примерно знаю, что должен делать скрипт, я не помню деталей с верхней части головы. То, что я описал выше, - это то, что я бы сделал в вашей ситуации.
есть плагин jQuery для этого, я использовал его с набором inline_form в Django 1.3, и он отлично работает, включая предварительное заполнение, добавление формы на стороне клиента, удаление и несколько наборов inline_formsets.
одним из вариантов было бы создать набор форм со всеми возможными формами, но изначально установить незапрошенные формы в hidden-ie,
display: none;. Когда необходимо отобразить форму, установите для ее отображения css значениеblockили все, что подходит.не зная более подробной информации о том, что делает ваш "Аякс", трудно дать более подробный ответ.
еще одна версия cloneMore, которая позволяет проводить выборочную дезинфекцию полей. Используйте его, когда вам нужно предотвратить стирание нескольких полей.
$('table tr.add-row a').click(function() { toSanitize = new Array('id', 'product', 'price', 'type', 'valid_from', 'valid_until'); cloneMore('div.formtable table tr.form-row:last', 'form', toSanitize); }); function cloneMore(selector, type, sanitize) { var newElement = $(selector).clone(true); var total = $('#id_' + type + '-TOTAL_FORMS').val(); newElement.find(':input').each(function() { var namePure = $(this).attr('name').replace(type + '-' + (total-1) + '-', ''); var name = $(this).attr('name').replace('-' + (total-1) + '-','-' + total + '-'); var id = 'id_' + name; $(this).attr({'name': name, 'id': id}).removeAttr('checked'); if ($.inArray(namePure, sanitize) != -1) { $(this).val(''); } }); newElement.find('label').each(function() { var newFor = $(this).attr('for').replace('-' + (total-1) + '-','-' + total + '-'); $(this).attr('for', newFor); }); total++; $('#id_' + type + '-TOTAL_FORMS').val(total); $(selector).after(newElement); }
есть небольшая проблема с функцией cloneMore. Поскольку он также очищает значение автоматически сгенерированных скрытых полей django, он заставляет django жаловаться, если вы пытаетесь сохранить набор форм с более чем одной пустой формой.
вот исправление:
function cloneMore(selector, type) { var newElement = $(selector).clone(true); var total = $('#id_' + type + '-TOTAL_FORMS').val(); newElement.find(':input').each(function() { var name = $(this).attr('name').replace('-' + (total-1) + '-','-' + total + '-'); var id = 'id_' + name; if ($(this).attr('type') != 'hidden') { $(this).val(''); } $(this).attr({'name': name, 'id': id}).removeAttr('checked'); }); newElement.find('label').each(function() { var newFor = $(this).attr('for').replace('-' + (total-1) + '-','-' + total + '-'); $(this).attr('for', newFor); }); total++; $('#id_' + type + '-TOTAL_FORMS').val(total); $(selector).after(newElement); }
Я думаю, что это гораздо лучшее решение.
как бы вы сделали динамический набор форм в Django?
делает вещи клонировать не делает:
- добавить форму, когда нет начальной формы
- лучше обрабатывает javascript в виде, например django-ckeditor
- сохранить исходные данные
@Paolo Bergantino
чтобы клонировать все прикрепленные обработчики, просто измените строку
var newElement = $(selector).clone();на
var newElement = $(selector).clone(true);предупреждения эту проблему.
Да, я бы также рекомендовал просто отображать их в html, если у вас есть конечное число записей. (Если вы этого не сделаете, вам придется использовать другой метод).
Вы можете скрыть их, как это:
{% for form in spokenLanguageFormset %} <fieldset class="languages-{{forloop.counter0 }} {% if spokenLanguageFormset.initial_forms|length < forloop.counter and forloop.counter != 1 %}hidden-form{% endif %}">тогда js действительно прост:
addItem: function(e){ e.preventDefault(); var maxForms = parseInt($(this).closest("fieldset").find("[name*='MAX_NUM_FORMS']").val(), 10); var initialForms = parseInt($(this).closest("fieldset").find("[name*='INITIAL_FORMS']").val(), 10); // check if we can add if (initialForms < maxForms) { $(this).closest("fieldset").find("fieldset:hidden").first().show(); if ($(this).closest("fieldset").find("fieldset:visible").length == maxForms ){ // here I'm just hiding my 'add' link $(this).closest(".control-group").hide(); }; }; }
потому что все ответы выше используют jQuery и делают некоторые вещи немного сложными я написал следующий скрипт:
function $(selector, element) { if (!element) { element = document } return element.querySelector(selector) } function $$(selector, element) { if (!element) { element = document } return element.querySelectorAll(selector) } function hasReachedMaxNum(type, form) { var total = parseInt(form.elements[type + "-TOTAL_FORMS"].value); var max = parseInt(form.elements[type + "-MAX_NUM_FORMS"].value); return total >= max } function cloneMore(element, type, form) { var totalElement = form.elements[type + "-TOTAL_FORMS"]; total = parseInt(totalElement.value); newElement = element.cloneNode(true); for (var input of $$("input", newElement)) { input.name = input.name.replace("-" + (total - 1) + "-", "-" + total + "-"); input.value = null } total++; element.parentNode.insertBefore(newElement, element.nextSibling); totalElement.value = total; return newElement } var addChoiceButton = $("#add-choice"); addChoiceButton.onclick = function() { var choices = $("#choices"); var createForm = $("#create"); cloneMore(choices.lastElementChild, "choice_set", createForm); if (hasReachedMaxNum("choice_set", createForm)) { this.disabled = true } };сначала вы должны установить auto_id для false и поэтому отключить дублирование идентификатора и имени. Поскольку входные имена должны быть уникальными в этой форме, вся идентификация выполняется с ними, а не с идентификаторами. Вы также должны заменить
form,typeи контейнер набора форм. (В приведенном выше примереchoices)
для кодеров, которые там охотятся за ресурсами, чтобы понять вышеуказанные решения немного лучше:
Динамические Наборы Форм Django
после прочтения приведенной выше ссылки документация Django и предыдущие решения должны иметь гораздо больше смысла.
как краткое резюме того, что я запутался: форма управления содержит обзор форм внутри. Вы должны держите эту информацию точной, чтобы Django знал о формах, которые вы добавляете. (Сообщество, пожалуйста, дайте мне предложения, если некоторые из моих формулировок здесь отключены. Я новичок в Джанго.)
Comments