skip to Main Content

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


  1. Chosen as BEST ANSWER

    I made a few changes. In the views, I made a logic that handles whether item_done is true or false.

    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
    

    In html a changed:

    <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>
    

    And 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){
    
        }
    
      });
    });
    

    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?


  2. 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!

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search