Я работаю над формой django , где пользователи могут динамически добавлять/удалить colors , размеры и Images при добавлении продукта.
- a продукт может иметь несколько цветов .
- Каждый color < /strong> может иметь несколько размеров .
- Каждый color может иметь множество изображений .
Пользователи должны иметь возможность динамически Добавить/удалить цвета, размеры и изображения с использованием javascript.
- Нажмите «Добавить цвет» должна создать новую форму цвета с собственными размер размер и разделения изображений .
- Нажмите "Добавить Размер " внутри цвета должен добавить новый размер только к этому конкретному цвету. Этот конкретный цвет.
- Нажмите «Удалить цвет» должен удалить конкретный цветовой раздел.
- только кнопка «Удалить цвет» работает.
- , "добавить size" и "добавить image" кнопки не работают. Кнопки. Forms < /strong> (они существуют).
Код: Выделить всё
"Add Color"
✔ Проверенные слушатели событий прикреплены с использованием console.log () < /code>.
✔ обеспечить клоненод (true) < /код> правильно копирует элементы.
✔ проверено на ошибки JavaScript в консоли (без ошибок синтаксиса). < /p>
django forms & formsetsforms (forms.py)
from django import forms
from django.forms import inlineformset_factory
from .models import VendorProduct, ProductColor, ProductSize, ProductImage, ColorImage
class ProductForm(forms.ModelForm):
class Meta:
model = VendorProduct
fields = ['title', 'category', 'base_price']
widgets = {
'title': forms.TextInput(attrs={'class': 'form-control'}),
'category': forms.Select(attrs={'class': 'form-control'}),
'base_price': forms.NumberInput(attrs={'class': 'form-control'}),
}
class ProductColorForm(forms.ModelForm):
class Meta:
model = ProductColor
fields = ['color_name', 'color_code']
widgets = {
'color_name': forms.TextInput(attrs={'class': 'form-control'}),
'color_code': forms.TextInput(attrs={'class': 'form-control'}),
}
class ProductSizeForm(forms.ModelForm):
class Meta:
model = ProductSize
fields = ['size_name', 'stock', 'price_increment']
widgets = {
'size_name': forms.TextInput(attrs={'class': 'form-control'}),
'stock': forms.NumberInput(attrs={'class': 'form-control'}),
'price_increment': forms.NumberInput(attrs={'class': 'form-control'}),
}
class ProductImageForm(forms.ModelForm):
class Meta:
model = ProductImage
fields = ['image']
widgets = {
'image': forms.FileInput(attrs={'class': 'form-control'}),
}
class ColorImageForm(forms.ModelForm):
class Meta:
model = ColorImage
fields = ['image']
widgets = {
'image': forms.FileInput(attrs={'class': 'form-control'}),
}
# Formsets
ProductColorFormSet = inlineformset_factory(VendorProduct, ProductColor, form=ProductColorForm, extra=1, can_delete=True)
ProductImageFormSet = inlineformset_factory(VendorProduct, ProductImage, form=ProductImageForm, extra=1, can_delete=True)
ProductSizeFormSet = inlineformset_factory(ProductColor, ProductSize, form=ProductSizeForm, extra=1, can_delete=True)
ColorImageFormSet = inlineformset_factory(ProductColor, ColorImage, form=ColorImageForm, extra=1, can_delete=True)
< /code>
шаблон HTML (add_product.html) < /strong> < /h3>
{% csrf_token %}
{{ form.as_p }}
{% for color_form in color_formset %}
{{ color_form.color_name.label_tag }}
{{ color_form.color_name }}
{{ color_form.color_code.label_tag }}
{{ color_form.color_code }}
Color Images
{{ color_image_formset.management_form }}
{% for image_form in color_image_formset %}
{{ image_form.image }}
{% endfor %}
Add Image
Sizes
{{ size_formset.management_form }}
{% for size_form in size_formset %}
{{ size_form.size_name.label_tag }}
{{ size_form.size_name }}
{{ size_form.stock.label_tag }}
{{ size_form.stock }}
{{ size_form.price_increment.label_tag }}
{{ size_form.price_increment }}
{% endfor %}
Add Size
Remove Color
{% endfor %}
Add Color
Save
< /code>
javascript (dynamic.js) < /strong> < /h3>
document.addEventListener("DOMContentLoaded", function () {
// Initialize indexes
let colorIndex = parseInt(document.querySelector('[name="colors-TOTAL_FORMS"]')?.value) || 0;
let imageIndex = parseInt(document.querySelector('[name="images-TOTAL_FORMS"]')?.value) || 0;
function addColor() {
let colorContainer = document.getElementById("colorContainer");
let totalForms = document.querySelector('[name="colors-TOTAL_FORMS"]');
if (!totalForms) {
console.error("Management form for colors-TOTAL_FORMS not found!");
return;
}
let newColor = document.querySelector(".color-item").cloneNode(true);
newColor.querySelectorAll("input, select").forEach(input => {
if (input.name) {
input.name = input.name.replace(/colors-\d+/g, `colors-${colorIndex}`);
input.removeAttribute("id"); // Remove duplicate IDs
input.value = ""; // Clear input values
}
});
// Attach event listeners to the new color block
newColor.querySelector(".remove-btn").addEventListener("click", function () {
removeColor(this);
});
newColor.querySelector(".add-size-btn").addEventListener("click", function () {
addSize(this);
});
colorContainer.appendChild(newColor);
totalForms.value = colorIndex + 1; // Update Django formset tracker
colorIndex++;
}
function addSize(button) {
let sizeContainer = button.closest(".color-item").querySelector(".sizeContainer");
let sizeIndex = sizeContainer.querySelectorAll(".size-item").length;
let newSize = sizeContainer.querySelector(".size-item").cloneNode(true);
newSize.querySelectorAll("input, select").forEach(input => {
if (input.name) {
input.name = input.name.replace(/sizes-\d+/g, `sizes-${sizeIndex}`);
input.removeAttribute("id");
input.value = ""; // Clear input values
}
});
sizeContainer.appendChild(newSize);
}
function removeColor(button) {
let colorItem = button.closest(".color-item");
colorItem.remove();
// Update colors-TOTAL_FORMS count
colorIndex--;
document.querySelector('[name="colors-TOTAL_FORMS"]').value = colorIndex;
}
function addImage() {
let imageContainer = document.getElementById("imageContainer");
let totalForms = document.querySelector('[name="images-TOTAL_FORMS"]');
if (!totalForms) {
console.error("Management form for images-TOTAL_FORMS not found!");
return;
}
let newImage = document.querySelector(".image-item").cloneNode(true);
newImage.querySelectorAll("input").forEach(input => {
if (input.name) {
input.name = input.name.replace(/images-\d+/g, `images-${imageIndex}`);
input.removeAttribute("id");
input.value = ""; // Clear input values
}
});
imageContainer.appendChild(newImage);
totalForms.value = imageIndex + 1; // Update Django formset tracker
imageIndex++;
}
// Attach event listeners dynamically
document.getElementById("addColorButton")?.addEventListener("click", addColor);
document.getElementById("addImageButton")?.addEventListener("click", addImage);
// Attach existing remove/add-size event listeners for initial elements
document.querySelectorAll(".remove-btn").forEach(button => {
button.addEventListener("click", function () {
removeColor(this);
});
});
document.querySelectorAll(".add-size-btn").forEach(button => {
button.addEventListener("click", function () {
addSize(this);
});
});
});
< /code>
Сводка проблем < /strong> < /h3>
Слушатели событий не были прикреплены правильно. Strong> JavaScript не использовал делегирование событий должным образом. - Сценарий, необходимый для обработки как статических, так и динамически добавленных элементов.
Подробнее здесь: https://stackoverflow.com/questions/794 ... and-images