I have a serious problem with checkboxes. I’m creating a ToDo style application and I want it to save the changes (item_done = False or True) when the checkbox is clicked.
I have implemented a JS script to make everything come out "smoothly", I am using jQuery.
Unfortunately the following error appears:
when clicking on the first item in the list, everything works smoothly but when clicking on the second, the change happens in the first and vice versa when clicking on the first the change happens in the second.
I can’t to figure out what I am doing wrong.
I have included the code below.
HTML with Django, list of items in task:
{% extends 'base.html' %}
{% block content %}
<br/><br/><br/>
<div class="container">
<div class="col-md-6 offset-md-3">
<div class="row">
<div class="card border-primary mb-3 text-center" style="max-width: 60rem;">
<div class="card-header bg-transparent ">{{ task.date }}</div>
{% if task.image %}
<img src="{{ task.image.url }}" class="card-img-top" alt="">
{% endif %}
<div class="card-body">
<h3 class="card-header bg-transparent">{{ task.title }}</h3>
<p class="card-text">{{ task.description }}</p>
</div>
<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>
</div>
<div>
<br/>
<button type="button" class="btn btn-primary">Add new item</button>
</div>
<div class="card-header bg-transparent">
<h3>Notes</h3>
</div>
<form>
<br/>
<div class="mb-3">
<textarea class="form-control" id="exampleFormControlTextarea1" rows="3"
placeholder="type something">
</textarea>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
<div class="card-body">
<a href="#" class="card-link">Card link</a>
<a href="#" class="card-link">Another link</a>
</div>
</div>
</div>
</div>
</div>
<br/><br/><br/>
<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>
{% endblock %}
JS code:
<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 views:
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
Here display task with "to do list". Every item has own id.
def view_task(request, pk):
if request.user.is_authenticated:
task = Task.objects.get(id=pk)
list_items = ToDoItem.objects.filter(task=task.id)
if request.user.id == task.task_user.id:
return render(request, 'view_task.html', {
'task': task,
'list_items': list_items
})
else:
messages.success(request, ("No authorisation!"))
return redirect('home')
else:
messages.success(request, ("You have to be logged in!"))
return redirect('login')
Task and ToDoItem model for understanding rest of code.
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
Please help me, I want to understand what I’m doing wrong and how I can fix it.
2
Answers
I made a few changes. In the views, I made a logic that handles whether item_done is true or false.
In html a changed:
And jQuery:
now the program correctly marks if the item is done from the database but does not read the unique id generated by {{forloop.counter}}.
It treats it as empty (I added the error in the JS comment). I've already looked for solutions to this problem but haven't found one - many people recommend using {{forloop.counter}} but it just doesn't work.
If I use {{forloop.counter}} with some text "checkbox-{{forloop.counter}}" JS read only {{checkbox}} and treat every element with the same ID (this is the same problem like the last).
What did I skip in the code?
You are creating multiple elements in your page with the same id (either ‘check-box’ or ‘un-check-box’) which is most likely where your problem is stemming from. You probably want to give the elements a specific class and then use document.getElementsByClassName to iterate through them. Oh, you’re using jQuery … iirc it’s
$('.class-name').each()
Either way, give each element a unique ID or no ID. Hope this helps!