Why do boxes become checked and then unchecked after clicking a “Check All” button?

Viewed 631

I have a JS function here that I'm trying to fix. This function gets triggered when the Check All button is clicked. When this button is clicked, all the items in the table should be checked and disabled. When I run this function and click the Check All button, all the items are selected briefly and then they go back to being unchecked. What am I doing wrong?

PS: I've tried adding e.preventDefault() and the end of the function and it doesn't work

$(document).ready(function () {
        $('body').on('change', '#checkAll', function (e) {
            if(this.checked){
                let boxes = $("input.promo-action-checkbox");
                boxes.toArray().forEach(element => {
                    console.log(element);
                    $(element).attr('disabled', 'disabled');
                    var name = $(element).data('name');
                    var number = $(element).data('number');
                    var user = $(element).data('user');
                    $.ajax({
                        method: 'post',
                        url: '/checker',
                        contentType: "application/json",
                        data: JSON.stringify({
                            name: name,
                            number: number,
                            user: user
                        }),
                        dataType: 'json',
                        error: function () {
                            $(element).attr('disabled', '');
                        }
                        })
                    
                });

How columns are made in html table:

 var table = $('#datatable').DataTable({
    pageLength: 20,
    lengthChange: false,
    language: {
      paginate: {
        previous: '<i class=\'fas fa-angle-left\'>',
        next: '<i class=\'fas fa-angle-right\'>'
      }
    },
    ordering: true,
    processing: true,
    serverSide: true,
    ajax: {
      url: '/table/',
      dataSrc: 'results'
    },
    columns: [
      {
        data: null,
        render: function (data, type, row, meta) {
          let rowHtml = '<div class="custom-control custom-checkbox mb-3"> <input class="custom-control-input promo-action-checkbox" id="checkbox-' + meta.row + '"  data-name="' + row.name + '" data-phone="' + row.number + '" data-admin="' + row.user + '" type="checkbox"'
          if (row.fulfilled) {
            rowHtml += ' disabled checked'
          }
          rowHtml += '> <label class="custom-control-label"  for="checkbox-' + meta.row + '"></label></div>'
          return rowHtml
        }
      },
      {
        searchable: true,
        data: 'name'
      }
    ]
  });

Here is what works however the "checkAll" box does not get ticked. Also this table is not inside a form.

$('#checkAll').click(function (e) {
 e.preventDefault()
    $('input.action-checkbox').prop('checked', this.checked);  
    $('input.action-checkbox').prop('disabled', true);
    return false;
})

Table Structure:

        <div class="table-responsive">
            <table class="table align-items-center table-flush table-striped" id="datatable-promo-code">
                <thead class="thead-light">
                <tr>
                    <th>Check All <input id = "checkAll"  type= "checkbox"></th>
                    <th>Name</th>
                    <th>Info</th>
                    <th>address</th>
                    <th>Email</th>
                    <th>Number</th>
                </tr>
                </thead>
                <tbody>
                <tr>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                    <td></td>
                </tr>
                </tbody>
            </table>
        </div>

2 Answers

Hard to tell because I can't actually run your code... but I would put a console.log into the render function just to look when it is being called. My guess would be that your table is being re-rendered before all requests done on your 'change' listener. If that is the case you need to wait all promises end before manually refreshing your page or re-rendering your table... Something like...

$(document).ready(function () {
  $('body').on('change', '#checkAll', async function (e) {
    if(this.checked) {
      let boxes = $("input.promo-action-checkbox");
      const promises = boxes.toArray().map(element => {
        return new Promise(function (resolve, reject) {
          $(element).attr('disabled', 'disabled');
          var name = $(element).data('name');
          var number = $(element).data('number');
          var user = $(element).data('user');
          $.ajax({
              method: 'post',
              url: '/checker',
              contentType: "application/json",
              data: JSON.stringify({
                  name: name,
                  number: number,
                  user: user
              }),
              dataType: 'json',
              error: function () {
                  $(element).attr('disabled', '');
                  reject();
              },
              success: function() {
                resolve()
              }
          });
        });
      });

      await Promise.all(promises);
      // refresh the page
      document.location.reload()
      // or re-render your table... no ideia how to that though...
      someMagicFunctionToReRenderYourTable()
    }
  });
});

It's not clear as to why all checkboxes need not only to be checked but also disabled. It's also not very clear as what the rest of the code is doing once that "button" is clicked. More importantly, is this <table> in a <form>? If so, then it's a possibility that the <form> resets itself (a default behavior at "submit" event)

Putting all of that aside, we can isolate the code into smaller portions. This way we can fix something (ie interactive elements like <input>) without breaking something else (ie AJAX POST request).

The example below will:

  1. Listen for the "change" event to fire only upon #checkAll
  2. Once triggered, the event handler lockCells() will run.
    $('#checkAll').on('change', lockCells);
    
  3. lockCells(event) {... passes the Event Object...
  4. ...check if #checkAll (this) is .checked or not.
    let status = this.checked ? true : false;
    
  5. ...then collect all <input> within any <table> and run the .prop() method on each of them.
     $('table input').prop(...)
    
  6. ...with the parameters of 'disabled' and 'checked' which Boolean value of true/false determined earlier at step #4
    {
      'disabled': status,
      'checked': status
    }
    

It's important to note: while method .attr() is used to manipulate attributes, .prop() is a better choice for properties especially with Boolean values (T/F). Refer to this question for details.

$('#checkAll').on('change', lockCells);

function lockCells(event) {
  let status = this.checked ? true : false;
  $('table input').prop({
    'disabled': status,
    'checked': status
  });
};
label {
  display: inline-block;
  padding: 3px 5px;
  border: 1px inset grey;
  border-radius: 6px;
  margin: 5px 10px;
  float: right;
  cursor: pointer;
}

label::before {
  content: 'Disable ';
}

#checkAll:checked+label::before {
  content: 'Enable ';
}

table input {
  display: block;
  width: 10ch;
}
<input id='checkAll' type='checkbox' hidden>
<label for='checkAll'> All Cells</label>
<table>
  <tbody>
    <tr>
      <td><input type='checkbox'></td>
      <td><input type='checkbox'></td>
      <td><input type='checkbox'></td>
      <td><input type='checkbox'></td>
    </tr>

    <tr>
      <td><input type='checkbox'></td>
      <td><input type='checkbox'></td>
      <td><input type='checkbox'></td>
      <td><input type='checkbox'></td>
    </tr>

    <tr>
      <td><input type='checkbox'></td>
      <td><input type='checkbox'></td>
      <td><input type='checkbox'></td>
      <td><input type='checkbox'></td>
    </tr>

    <tr>
      <td><input type='checkbox'></td>
      <td><input type='checkbox'></td>
      <td><input type='checkbox'></td>
      <td><input type='checkbox'></td>
    </tr>

  </tbody>
</table>

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

Related