Чекбоксы в Django и JS не работают должным образом
У меня серьезная проблема с чекбоксами. Я создаю приложение в стиле ToDo и хочу, чтобы оно сохраняло изменения (item_done = False или True) при нажатии на чекбокс.
Я реализовал JS-скрипт, чтобы все выходило "гладко", я использую jQuery. К сожалению, возникает следующая ошибка: при нажатии на первый элемент в списке все работает плавно, но при нажатии на второй изменение происходит в первом и наоборот, при нажатии на первый изменение происходит во втором.
Я не могу понять, что я делаю не так.
Я включил код ниже.
HTML с Django, список элементов в задании:
<div class="card-header bg-transparent" ><h3>To-do list</h3></div>
<div class="text-start">
<br/>
<ul class="list-group">
{% if list_items %}
{% for item in list_items %}
{% if item.item_done %}
<li class="list-group-item">
<input class="form-check-input" type="checkbox" checked="checked" value="{{ item.id }}" id="un-check-box">
{{ item.title }}
</li>
{% else %}
<li class="list-group-item">
<input class="form-check-input" type="checkbox" value="{{ item.id }}" id="check-box">
{{ item.title }}
</li>
{% endif %}
{% endfor %}
{% else %}
<p class="text-center">the task list is empty</p>
{% endif %}
</ul>
JS-код:
<script>
// Check if checkbox pressed
$(document).on('click', '#check-box', function(e){
e.preventDefault();
$.ajax({
type: 'POST',
url: '{% url 'check_boxes' %}',
data: {
item_id: $('#check-box').val(),
csrfmiddlewaretoken: '{{ csrf_token }}',
action: 'post'
},
success: function(json){
console.log(json)
document.getElementById("check-box").checked = true;
location.reload();
},
error: function(xhr, errmsg, err){
}
});
});
// Uncheck boxes
$(document).on('click', '#un-check-box', function(e){
e.preventDefault();
$.ajax({
type: 'POST',
url: '{% url 'uncheck_boxes' %}',
data: {
item_id: $('#un-check-box').val(),
csrfmiddlewaretoken: '{{ csrf_token }}',
action: 'post'
},
success: function(json){
console.log(json)
document.getElementById("un-check-box").checked = false;
location.reload();
},
error: function(xhr, errmsg, err){
}
});
});
</script>
Виды Python:
def check_boxes(request):
if request.POST.get('action') == 'post':
item_id = int(request.POST.get('item_id'))
item = get_object_or_404(ToDoItem, id=item_id)
item.item_done = True
item.save()
response = JsonResponse({'item': item.item_done,
'item_id' : item_id
})
return response
def uncheck_boxes(request):
if request.POST.get('action') == 'post':
item_id = int(request.POST.get('item_id'))
item = get_object_or_404(ToDoItem, id=item_id)
item.item_done = False
item.save()
print(item.item_done)
response = JsonResponse({'item': item.item_done,
'item_id': item_id
})
return response
Модель задачи и ToDoItem для понимания остального кода.
class Task(models.Model):
task_user = models.ForeignKey(AppUser, on_delete=models.CASCADE)
title = models.CharField(max_length=50)
description = models.TextField(max_length=1000)
date = models.DateField(default=datetime.datetime.today)
image = models.ImageField(upload_to='uploads/task/', blank=True)
is_done = models.BooleanField(default=False)
def __str_(self):
""" return object name"""
return self.title
class ToDoItem(models.Model):
title = models.CharField(max_length=100)
task = models.ForeignKey(Task, on_delete=models.CASCADE)
item_done = models.BooleanField(default=False)
def __str__(self):
"""return object name"""
return self.title
Пожалуйста, помогите мне, я хочу понять, что я делаю не так и как я могу это исправить.
Вы создаете на странице несколько элементов с одинаковым id (либо 'check-box', либо 'un-check-box'), что, скорее всего, и является причиной вашей проблемы. Вероятно, вы хотите присвоить элементам определенный класс, а затем использовать document.getElementsByClassName для их перебора. О, вы используете jQuery... я помню, что это $('.class-name').each()
В любом случае присвойте каждому элементу уникальный ID или не присваивайте его. Надеюсь, это поможет!
Я сделал несколько изменений. В представлениях я сделал логику, которая обрабатывает, является ли item_done истиной или ложью.
def check_boxes(request):
if request.POST.get('action') == 'post':
item_id = int(request.POST.get('item_id'))
item = get_object_or_404(ToDoItem, id=item_id)
if item.item_done:
item.item_done = False
item.save()
else:
item.item_done = True
item.save()
response = JsonResponse({'item': item.item_done,
'item_id': item_id
})
return response
В html a изменилось:
<ul class="list-group">
{% if list_items %}
{% for item in list_items %}
<li class="list-group-item">
<input type="checkbox" {{ item.item_done|yesno:"checked," }} value="{{ item.id }}"
class="check-box" id="{{forloop.counter}}">
{{ item.title }}
</li>
{% endfor %}
{% else %}
<p class="text-center">the task list is empty</p>
{% endif %}
</ul>
И jQuery:
// check boxes
$('.check-box').on('click', function(e){
e.preventDefault();
$.ajax({
type: 'POST',
url: '{% url 'check_boxes' %}',
data: {
item_id: $(this).val(),
csrfmiddlewaretoken: '{{ csrf_token }}',
action: 'post'
},
success: function(json){
console.log(json)
let checkBox
//bellow: Uncaught TypeError: checkBox is null
checkBox = document.getElementById("{{forloop.counter}}")
if (checkBox.checked === true){
checkBox.checked = false;
} else {
checkBox.checked = true;
}
location.reload();
},
error: function(xhr, errmsg, err){
}
});
});
теперь программа правильно отмечает, если элемент выполнен из базы данных, но не считывает уникальный id, сгенерированный {{forloop.counter}}.
Он воспринимает его как пустой (я добавил ошибку в комментарий JS). Я уже искал решение этой проблемы, но так и не нашел - многие рекомендуют использовать {{forloop.counter}}, но это просто не работает.
Если я использую {{forloop.counter}} с некоторым текстом "checkbox-{{forloop.counter}}" JS читает только {{checkbox}} и обрабатывает каждый элемент с тем же ID (это та же проблема, что и в предыдущем случае).
Что я пропустил в коде?
Я нашел решение.
Может, это и не профи (а может, и профи), но это работает и решило немало моих проблем.
По какой-то причине JS не читал ни {{foorloop.counter}}, ни id, и я зависел от выбора и отмены выбора конкретного чекбокса.
Потом я понял, что item_id передается в json и отображается в контрольном журнале, поэтому я передал значение item в дополнение к item id, а затем извлек его из json с помощью метода getElementById. Далее условие if else и флажки на странице работают так, как я хотел. Я также протестировал в другом браузере, удалил историю и повторил - все работает!
Ниже приведены изменения:
HTML и теги Django:
<li class="list-group-item">
<input class="form-check-input me-1 check-box" type="checkbox" {{ item.item_done|yesno:"checked," }} value="{{ item.id }}"
id="{{ item.id }}">
{{ item.title }}
</li>
jQuery
$('.check-box').on('click', function(e){
e.preventDefault();
$.ajax({
type: 'POST',
url: '{% url 'check_boxes' %}',
data: {
item_id: $(this).val(),
csrfmiddlewaretoken: '{{ csrf_token }}',
action: 'post'
},
success: function(json){
console.log(json)
let itemId
itemId = json.item_id
let checkBox
checkBox = document.getElementById(itemId)
if (checkBox.checked === true){
checkBox.checked = false;
} else {
checkBox.checked = true;
};
location.reload();
},
error: function(xhr, errmsg, err){
}
});
});
view
def check_boxes(request):
if request.POST.get('action') == 'post':
item_id = int(request.POST.get('item_id'))
item = get_object_or_404(ToDoItem, id=item_id)
if item.item_done:
item.item_done = False
item.save()
else:
item.item_done = True
item.save()
response = JsonResponse({'item': item.item_done,
'item_id': item_id
})
return response