ссылки на элементы, возвращаемые функциями .find_element_*
, взяты из начальной загрузки страницы. Когда вы click()
, вы уходите от начальной страницы, делая все ссылки на элементы устаревшими. Вам нужно будет снова вызвать find_elements, прежде чем отправлять ключи новым элементам.
Это - то, как я делаю это, с помощью [1 124] 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
аргумент - то, так, чтобы мы могли обновить management_form
поле, особенно TOTAL_FORMS
, а также фактические поля формы. Если у Вас будет formset полный из, скажем, Client
модели, то поля управления будут иметь идентификаторы id_clients-TOTAL_FORMS
и id_clients-INITIAL_FORMS
, в то время как поля формы будут в формате [1 116] с [1 117] являющийся числом формы, начиная с [1 118]. Таким образом с type
аргумент cloneMore
функция смотрит на то, сколько там в настоящее время формы, и проходит каждый вход и маркировку в новой форме, заменяющей все поле, names/ids от чего-то как [1 121] к [1 122] и так далее. После того, как это будет закончено, это обновляет TOTAL_FORMS
поле для отражения новой формы и добавляет его до конца набора.
Эта функция особенно полезна мне, потому что способ, которым это - установка, это позволяет мне использовать его всюду по приложению, когда я хочу обеспечить больше форм в formset, и не делает меня, должен иметь скрытую "шаблонную" форму для дублирования, пока я передаю его имя formset и формат, в котором размечаются формы. Надежда это помогает.
Моделируйте и подражайте:
<input>
поля. <input>
измененные поля. , В то время как я действительно знаю, formsets используют особенный скрытый <input>
поля и знают приблизительно, что должен сделать сценарий, я не вспоминаю детали первое, что пришло на ум. То, что я описал выше, - то, что я сделал бы в Вашей ситуации.
Я имею , отправил отрывок из приложения, я продолжил работать некоторое время назад. Подобный Паоло, но также и позволяет Вам, удаляют формы.
Одна опция состояла бы в том, чтобы создать formset с каждой возможной формой, но первоначально установить необязательные формы на скрытый - т.е., display: none;
. Когда необходимо отобразить форму, установить, это - дисплей CSS к block
или независимо от того, что является соответствующим.
Без знают больше деталей того, что делает Ваш "Ajax", трудно дать более подробный ответ.
Paolo's suggestion works beautifully with one caveat - the browser's back/forward buttons.
The dynamic elements created with Paolo's script will not be rendered if the user returns to the formset using the back/forward button. An issue that may be a deal breaker for some.
Example:
1) User adds two new forms to the formset using the "add-more" button
2) User populates the forms and submits the formset
3) User clicks the back button in the browser
4) Formset is now reduced to the original form, all dynamically added forms are not there
This is not a defect with Paolo's script at all; but a fact of life with dom manipulation and browser's cache.
I suppose one could store the values of the form in the session and have some ajax magic when the formset loads to create the elements again and reload the values from the session; but depending on how anal you want to be about the same user and multiple instances of the form this may become very complicated.
Anyone has a good suggestion for dealing with this?
Thanks!
@Paolo Bergantino
to clone all the handlers attached just modify the line
var newElement = $(selector).clone();
for
var newElement = $(selector).clone(true);
to prevent this problem.
Есть небольшая проблема с функцией 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);
}