Я нашел рабочее решение для указанной задачи. Это связано с тем, как работает jquery.formset.js
. Если кто-то другой ударит по этому кирпичу, вот как я его исправил.
Когда вы нажимаете «Добавить элемент», jquery.formset.js
знает контейнер, который содержит первоначально отображаемые формы форм. Из этого следует, этот алгоритм:
- Clone один из formset форм
- добавить его в DOM после последнего formset формы контейнера
- Пересчитать и изменить
id
, name
и другие атрибуты
- и т.д.
flexselect.js
с другой стороны работает, ища указанные select
теги в DOM и добавляя к ним некоторые данные.
Добавление клонирования select
s, которое уже было обработано обратно в DOM, было причиной неожиданного поведения.
Я не мог придумать лучшего способа изменить исходный код jquery.formset.js
. Я изменил его следующим образом (начиная со строки 75):
$addBtn.click(function() {
var nextIndex = parseInt($('#id_' + options.prefix + '-TOTAL_FORMS').val());
var row = $('.' + options.formCssClass + ':first').clone(true).get(0);
$(row).removeAttr('id').insertAfter($('.' + options.formCssClass + ':last'));
$(row).find('input,select,textarea,label').each(function() {
updateElementIndex(this, options.prefix, nextIndex);
// If this is a checkbox or radiobutton, set uncheck it.
// Fix for Issue 1, reported by Wilson.Andrew.J:
var elem = $(this);
if (elem.is('input:checkbox') || elem.is('input:radio')) {
elem.attr('checked', false);
} else {
elem.val('');
}
});
var formCount = nextIndex + 1;
$('#id_' + options.prefix + '-TOTAL_FORMS').val(formCount);
// If we've got more than one form, enable delete buttons:
if (formCount > 1) { $('a.' + options.deleteCssClass).show(); }
// If a post-add callback was supplied, call it with the added form:
if (options.added) options.added($(row));
return false;
});
to .....
$addBtn.click(function() {
var nextIndex = parseInt($('#id_' + options.prefix + '-TOTAL_FORMS').val());
var row = $('.' + options.formCssClass + ':first').clone(true).get(0);
// formset.js does not know that the form has been manipulated by flexselect.js. By cloning the form as
// it is, it clones elements that have already had events and stuff attached to them. This is what
// causes the weird behavior with flexselect.js
// first we'll remove the class and style attributes
$(row).find('select.flexselect').last().removeAttr("class style");
// next, remove the input field that was previously generated by flexselect
$(row).find('input.flexselect').remove();
$(row).removeAttr('id').insertAfter($('.' + options.formCssClass + ':last'));
$(row).find('input,select,textarea,label').each(function() {
updateElementIndex(this, options.prefix, nextIndex);
// If this is a checkbox or radiobutton, set uncheck it.
// Fix for Issue 1, reported by Wilson.Andrew.J:
var elem = $(this);
if (elem.is('input:checkbox') || elem.is('input:radio')) {
elem.attr('checked', false);
} else {
elem.val('');
}
});
// At this point, formset.js has done most of the major lifting. We can now add the attributes that
// flexselect.js needs which is just a select with a CSS class called 'flexselect'.
$(row).find("select").attr("class", "flexselect");
var formCount = nextIndex + 1;
$('#id_' + options.prefix + '-TOTAL_FORMS').val(formCount);
// If we've got more than one form, enable delete buttons:
if (formCount > 1) { $('a.' + options.deleteCssClass).show(); }
// If a post-add callback was supplied, call it with the added form:
if (options.added) options.added($(row));
return false;
});
А потом я настроил инициализирующий код, таким образом:
<script type="text/javascript">
jQuery(document).ready(function($) {
$("select.flexselect").flexselect();
$(".inline.{{ posting_form.prefix }}").formset({
prefix: "{{ posting_form.prefix }}",
addText: "Add item",
deleteText: "Remove",
added: function(){
$('select.flexselect').last().removeData("flexselect");
$('select.flexselect').flexselect();
},
});
});
</script>
Это, как я установил его. Надеюсь, это поможет кому-то другому.