Layout DataTable DOM and CheckBox integration

Layout DataTable DOM and CheckBox integration

LoomesLoomes Posts: 13Questions: 1Answers: 0

I understand from https://datatables.net/reference/option/dom that CheckBoxes (for instance to filter certain data, like in https://datatables.net/forums/discussion/57221/how-do-i-add-checkbox-filtering-to-a-datatable) are not among it's elements.

Issue is that I need to add some checkboxes right between DataTable's length changing input control and filtering input panel but I did not manage to place some custom HTML into the table's DOM section. All examples I've found so far are placing checkboxes above the whole table but layout-wise I can't afford that since I also have a fair amount of columnToggle buttons in a second row.

So what is the best way to place checkboxes among DataTable's elements? Thanks.

My element declaration so far:

"dom": 
"<'dt--top-section'<'row'<'col-12 col-sm-6 d-flex justify-content-sm-start justify-content-center'l><'col-12 col-sm-6 d-flex justify-content-sm-end justify-content-center mt-sm-0 mt-3'f>>>" +
"<'dt--top-section'<'row'<'col-sm-12 col-md-12 d-flex justify-content-md-start justify-content-center'B>>>" +
"<'table-responsive'tr>" + 
"<'dt--bottom-section d-sm-flex justify-content-sm-between text-center'<'dt--pages-count mb-sm-0 mb-3'i><'dt--pagination'p>>",
 buttons: [ {
   extend: 'columnsToggle',
   columns: [0,1,2,3,4,5,6,7,8,9]
} ],     

This question has an accepted answers - jump to answer

Answers

  • kthorngrenkthorngren Posts: 21,344Questions: 26Answers: 4,954
    Answer ✓

    See if this example helps to place the checkboxes within the Datatables dom elements.

    Kevin

  • LoomesLoomes Posts: 13Questions: 1Answers: 0

    That's exactly what I was looking for - thanks! :)

  • LoomesLoomes Posts: 13Questions: 1Answers: 0

    Seems I have to add the statesave for checkboxes to the data for stateSaveCallback and stateLoadCallback somehow. Is there an example of how to do it?

  • kthorngrenkthorngren Posts: 21,344Questions: 26Answers: 4,954

    You would use stateSaveParams and stateLoadParams instead. One option might be to get the checked checkboxes in the stateSaveParams and save them to an object added to the data variable. In stateLoadParams use that variable to check the appropriate checkboxes.

    I updated a checkbox search example to show this.
    https://live.datatables.net/dayopulu/1/edit

    You will need to refactor the code to match your checkboxes. Note it only saves the Position checkboxes not the Office.

    Kevin

  • LoomesLoomes Posts: 13Questions: 1Answers: 0

    Thanks a lot, I will try tomorrow and report back.

  • LoomesLoomes Posts: 13Questions: 1Answers: 0

    Weird things: The stateSaveParams is working well but in stateLoadParams I am not able to get a hold of my checkboxes. In my

    "stateLoadParams": function (settings, data) {
      console.log(data); // says "checked: 4", all good.
      $('input.filter[type="checkbox"]').each(function() {
        console.log($(this)); // nothing, not even entering loop once.                   
      });
    },
    

    I tried other parameters like input:checkbox[name="region"] which also work flawlessly for stateSaveParams but find no elements in stateLoadParams. Maybe the cause is that I integrated my checkboxes in the DataTable UI with

                $('div.toolbar').html(
                 '<input type="checkbox" class="filter" name="region" value="JP" checked/><label>Japan</label>' +
                 '<input type="checkbox" class="filter" name="region" value="US" checked/><label>USA</label>' +
                 '<input type="checkbox" class="filter" name="region" value="EU" checked/><label>Europe</label>'
                );
    

    but I don't see why stateSaveParams finds them and stateLoadParams doesn't. My stateSaveParams works and looks like

    "stateSaveParams": function (settings, data) {
      console.log(data); // shows expected values.
      var checked = [];
      $('input.filter[type="checkbox"]').each(function() {
        checked.push($(this).val());
        console.log($(this).val()); // shows expected values.
      });
      data.checked = checked;
      console.log(data); // shows expected values.
    },
    
  • kthorngrenkthorngren Posts: 21,344Questions: 26Answers: 4,954

    It's possible its a timing issue where stateLoadParams runs before the dom option builds the HTML. Instead of using stateLoadParams you might need to use state.loaded() in the -event init` event, like the example in the docs.

    Kevin

  • LoomesLoomes Posts: 13Questions: 1Answers: 0

    As I understand from https://datatables.net/reference/event/init the init event is fired at the same point as initComplete, so I put a test to my initComplete to see if it's a runtime issue:

    initComplete: function() {
    console.log(this.api().state.loaded()); // as expected, "checked: 4" etc.
    
    $('input.filter[type="checkbox"]').each(function() {
      console.log('checkbox found');  // finds nothing.
    });
    $('input:checkbox[name="region"]').each(function() {
      console.log('checkbox found');  // finds nothing.
    });
    $('input').each(function() {
      console.log('input found');   // finds 2 input fields.
    });
    $('div').each(function() {  // finds 30+ div.
      console.log('div found');                  
    });  
    

    So the checkboxes are not found after init complete. I have no idea what is going on here Oo

  • LoomesLoomes Posts: 13Questions: 1Answers: 0

    For testing purposes I've put the unchanged checkboxes above the DataTable, out of it's layout, and they are found immediately by $('input.filter[type="checkbox"]') and $('input:checkbox[name="region"]').
    So I guess it's safe to say that stateLoadParams has some kind of issue finding DT layout integrated elements. Here's the code how I integrated the elements:

    "dom":
      "<'dt--top-section'<'row'" +
      "<'col-12 col-sm-1 d-flex justify-content-sm-start justify-content-center'l>" +              
      "<'col-12 col-sm-6 d-flex justify-content-sm-start justify-content-center'<'toolbar'>>" +
      "<'col-12 col-sm-5 d-flex justify-content-sm-end justify-content-center'f>>>" +
      "<'dt--top-section'<'row'<'col-sm-12 col-md-12 d-flex justify-content-md-start justify-content-center'B>>>" +
      "<'table-responsive'tr>" + 
      "<'dt--bottom-section d-sm-flex justify-content-sm-between text-center'<'dt--pages-count  mb-sm-0 mb-3'i><'dt--pagination'p>>",
    

    and outside the DT definition but in the same file:

    $('div.toolbar').html(
      '<input type="checkbox" class="filter" name="region" value="JP" checked/><label>Japan</label>' +
      '<input type="checkbox" class="filter" name="region" value="US" checked/><label>USA</label>' +
      '<input type="checkbox" class="filter" name="region" value="EU" checked/><label>Europe</label>'
    );
    
  • kthorngrenkthorngren Posts: 21,344Questions: 26Answers: 4,954
    edited April 2023

    Try moving this:

    $('div.toolbar').html(
      '<input type="checkbox" class="filter" name="region" value="JP" checked/><label>Japan</label>' +
      '<input type="checkbox" class="filter" name="region" value="US" checked/><label>USA</label>' +
      '<input type="checkbox" class="filter" name="region" value="EU" checked/><label>Europe</label>'
    );
    

    Into initComplete before the code that loops through the saved checked checkboxes. You might want to remove the checked attribute or they will always show checked unless you specifically uncheck them. If you have ajax loaded data there might be.a delay in showing the checkboxes.

    I think the problem is all the above options are trying to access the checkboxes before the $('div.toolbar').html(..) code executes.

    Kevin

  • LoomesLoomes Posts: 13Questions: 1Answers: 0

    I moved the checkboxes to initComplete and they can be found now. But there is the issue now that initComplete is executed right after stateLoadParams. If the checkboxes have the checked attribute or not -- their init triggers stateSaveParams and overwrites the last checked states done by the user. So they are all checked or none, depending on their default. Any idea of how to approach this?

  • kthorngrenkthorngren Posts: 21,344Questions: 26Answers: 4,954

    Assuming you are using state.loaded() in initComplete to set the checked checkboxes you will want to no do this in stateLoadParams. Maybe you can build a simple test case showing what you are trying so we can help debug.
    https://datatables.net/manual/tech-notes/10#How-to-provide-a-test-case

    Kevin

  • LoomesLoomes Posts: 13Questions: 1Answers: 0

    Here's the test case. I left out the actual filtering done by the checkboxes (which works fine) to keep it simple. The checkboxes are shown at my website but not at LiveTables. I hope this can be fixed somehow: https://live.datatables.net/micudita/1/

  • kthorngrenkthorngren Posts: 21,344Questions: 26Answers: 4,954
    edited April 2023

    You are getting this error in the console.log:

    Uncaught TypeError: (intermediate value) is not a function

    You have a syntax error with the Datatables init code. The test case template uses the non-jQuery initialization. I commented out columnDefs because its trying to access columns that don't exist and doesn't affect the test case. I moved the stateLoadParams code to initComplete.

    I removed the default checked attribute from the checkboxes. After loading the state and setting the checkboxes that state needs to be updated to reflect the changes. Otherwise you can get into a situation where stateSaveParams will find no checked checkboxes due to the initial state.

    I created a checkbox change event handler that just calls draw() to simulate the search and forces a state save.
    https://live.datatables.net/tidunube/1/edit

    Kevin

  • LoomesLoomes Posts: 13Questions: 1Answers: 0

    Thanks a lot but there's on more issue resulting: Having all checkboxes unchecked is a different use case, because they are meant for optionally reducing(!) data, not enhancing -- therefore they need to be initially all checked.

    I have achieved that using the change event but it only works for manual click on the checkboxes, meaning the checkboxes are correctly un/checked after page reload but all the data is initially shown as if they were all checked.

    The cause of this is that $(this).prop('checked', true/false).trigger('change'); does not trigger the change event automatically by page reload and thus all the records are displayed in the DT. I read about .trigger('change') and this seems to be the most up to day way to go for triggering events from JS. Not working for me though: https://live.datatables.net/tidunube/2/

  • kthorngrenkthorngren Posts: 21,344Questions: 26Answers: 4,954

    You are getting this error:

    Uncaught TypeError: Cannot read properties of null (reading 'checked')

    The .trigger('change') isn't going to have the change events run since those are applied after.

    therefore they need to be initially all checked.

    You could check the value of checked fromvar checked = api.state.loaded().checked;` to see if its an empty array. If empty then set all the checkboxes as checked. If not then set them all unchecked and let the loop check them.

    I may have missed it but what exactly happens in your checkbox change event handler? Maybe update the test case to show this. If its using column().search() then I wold expect that search to be saved with state save. If using a search plugin then maybe call draw() at the end of initComplete.

    Kevin

  • LoomesLoomes Posts: 13Questions: 1Answers: 0

    Okay, will try that. How are you getting this error, there is nothing shown at my console in Firefox. I'm just using UltraEdit for syntax highlighting, are you using a dev environment for JavaScript you can recommend? Seems I'm missing some useful compiler feedback.

  • kthorngrenkthorngren Posts: 21,344Questions: 26Answers: 4,954

    How are you getting this error,

    I'm using the Chrome console (not the JS BIN console tab).

    Kevin

  • LoomesLoomes Posts: 13Questions: 1Answers: 0

    Okay, based on your help, I got the checkboxes now keeping their state after page reload and the DT is according to their config. But there is one last thing that worked before when I was using stateSaveCallback and stateLoadCallback and is not working anymore with stateSaveParams and initComplete:

    To be able to use my DT with different datasets and URLs, I overruled the save/load handlers with localStorage.setItem( '#example' + settings.sInstance, JSON.stringify(data) ); and return JSON.parse( localStorage.getItem( '#example' + settings.sInstance ) );. I tried using them now and can't make it work. I guess the idea still works but where and how to put these lines?

  • kthorngrenkthorngren Posts: 21,344Questions: 26Answers: 4,954

    Can you update the test case to show the issue?

    Kevin

  • kthorngrenkthorngren Posts: 21,344Questions: 26Answers: 4,954
    edited April 2023

    I built a test case with your localStorage statements and it seems to be working:
    https://live.datatables.net/tidunube/5/edit

    I did find the source of the Uncaught TypeError: Cannot read properties of null (reading 'checked') error. If you clear the state then the loaded state is null so the api.state.loaded().checked fails. Need to add some checks for this. You probably didn't see this error as it would occur only if the state wasn't saved, normally the very first time its run.

    If this doesn't help then please update the test case to show the issue.

    Kevin

  • LoomesLoomes Posts: 13Questions: 1Answers: 0

    Hey thanks a lot for your test case. Since I'm also using Chrome console now I figured the error out yesterday and also could make the localStorage.setItem thing work again. I thought stateSave/LoadCallback could not be used in the same DT with stateSave/LoadParam but I found out it works by accident. But I will look into your test case anyways, especially the code in the .on('change' function looks way better than mine.

    Again, thanks loads for your awesome support!!

Sign In or Register to comment.