Preventing child nested datatable from displaying

Preventing child nested datatable from displaying

menashemenashe Posts: 121Questions: 28Answers: 1

I have a (large) table with items. In a second table, I have implemented a parent-child relationship between items. When I display my main table, items that are children do not display; when I select an item that is a parent, then the children items appear below that parent in a "nested" child Datatable.

How do I implement a mechanism where if the PHP request return no children, then I do not attempt to display the child table (and especially not get the create/edit/delete buttons)?

Thanks

Answers

  • kthorngrenkthorngren Posts: 19,891Questions: 26Answers: 4,691
    Answer ✓

    Sounds like you are doing something like this blog example. The child table, in the example, looks like this:

    var usersTable = $('#users').DataTable( {
        dom: 'Bfrtip',
    ....
    });
    

    One option might be to use the xhr event for the child Datatable and show/hide the div that the child table is in. For example:

    var usersTable = $('#users')
    .on('xhr.dt', function ( e, settings, json, xhr ) {
            if ( json.data.length === 0 ) {
              $('#my-dt-div').hide();
            } else {
              $('#my-dt-div').show();
            }
        } )
    .DataTable( {
        dom: 'Bfrtip',
    

    Where #my-dt-div is the div container containing the child table. I didn't test this code so it me need some debugging to get working.

    Kevin

  • menashemenashe Posts: 121Questions: 28Answers: 1

    Kevin,

    Thank you! I had seen that example over the last year or so, but... forgot!

    That looks as though it will answer my question. I have to run, so I'll investigate later.

  • menashemenashe Posts: 121Questions: 28Answers: 1

    Kevin,

    That worked great!

    Can I somehow prevent the create/edit/delete buttons from appearing if the length is 0?

  • kthorngrenkthorngren Posts: 19,891Questions: 26Answers: 4,691

    Use buttons().container() to get the button's container element. Then use show() and hide(). For example:
    https://live.datatables.net/kiyowore/1/edit

    Keivn

  • menashemenashe Posts: 121Questions: 28Answers: 1

    Thanks! I have not yet implemented it, but you have an amazingly robust framework!

  • menashemenashe Posts: 121Questions: 28Answers: 1

    Kevin,

    That, plus the code from the Parent/Child example get rid of everything.

                        subitemsTable.buttons().container().hide();
                        
                        let table = $("table", row.child());
                        table.detach();
                        table.DataTable().destroy();
    
                        // And then hide the row
                        row.child.hide();
    

    BUT... there is a "flash"--it appears and then disappears!

    With Editor Forms, there are many cancellable events.

    Is there anything like that for Datatables--cancel before the Buttons and empty table are drawn?

    When I select a line, such as in the image below, I get the following (and then it quickly disappears):

    When there are no Sub Item records I would like to see... NOTHING!

  • kthorngrenkthorngren Posts: 19,891Questions: 26Answers: 4,691

    Based on your code snippet it doesn't seem like you are using the example I initially linked to. Instead it sounds like you are doing something like this child detail rows blog. Assuming you are following this guide you can probably update the createChild() function to check how many rows are in the table. If there are no rows then don't use row.child( table ).show();. The full solution might not be that simplistic.

    If this doesn't help then post all of the Datatables related Javascript code so we can see what you are doing.

    Kevin

  • menashemenashe Posts: 121Questions: 28Answers: 1

    You are correct, because your example has the line with "No Data Available in Table" at all times!

    But I think that I may not understand something!

    In createChild()

    function createChild ( row ) {
        // This is the table we'll convert into a DataTable
        var table = $('<table class="display" width="100%"/>');
     
        // Display it the child row
        row.child( table ).show();
     
        // Initialise as a DataTable
        var usersTable = table.DataTable( {
            // ...
        } );
    }
    

    The var line creates HTML for the table.
    Then, row.child( table ).show(); displays that empty table.
    Only after that does table.DataTable create the data for the table.

    How do I know before the row.child( table ).show(); that there is no data??

  • kthorngrenkthorngren Posts: 19,891Questions: 26Answers: 4,691

    You can move the row.child( table ).show(); inside initComplete of the child table. For example:

    initComplete: function(settings, json) {
      var api = this.api();
      var count = api.rows().count();
    
      if ( count > 0 ) {
        row.child( table ).show();
        api.columns.adjust();
      }
    }
    

    Use count() to count the number of rows. Use columns.adjust() to recalculate the column widths after the hidden table is shown. See this example for details.

    Kevin

  • menashemenashe Posts: 121Questions: 28Answers: 1

    I had tried that last night, but without the row.child( table ).show(); in the "normal" place, initComplete is never triggered.

  • allanallan Posts: 60,962Questions: 1Answers: 9,939 Site admin

    Perhaps you can link to a test case showing the issue please.

    Allan

  • menashemenashe Posts: 121Questions: 28Answers: 1

    What seems to work is display: none in the CSS. However, I have only been able to place it on the subitems wrapper.

    How can I place that CSS on the outer-most <tr> for the sub item?

    ![](https://datatables.net/forums/uploads/editor/66/en62l3pq66lc.png "")
    
  • menashemenashe Posts: 121Questions: 28Answers: 1

    Yes, working perfectly--except for that little bit of padding from the <tr>.

    Please advise how I can access it to modify the CSS display property of that outermost <tr>.

  • menashemenashe Posts: 121Questions: 28Answers: 1

    Hi Allan,

    Sorry for the delay. I have included a test case: https://live.datatables.net/loborahi/5/.

    It does generate an error (which can be ignored), but it does show the functionality that I am trying to achieve!

    So... if you click on the header column (the arrow), you will see the expanded information. When you click again, the child row closes completely as if it was never there.

    If you click anywhere else on the row (what I refer to as selecting the row), you will see that a small space opens up (and the error, because no sub item child data is generated).

    In my app, I do generate subitems, but sometimes there are none!

    When I "select", how can I not even have that tiny space (from the

                // Display the child row
                row.child(table).show();
    
    

    statement) not appear if no records will be generated for the sub items??

    Thanks!

  • kthorngrenkthorngren Posts: 19,891Questions: 26Answers: 4,691
    edited December 2023

    There are a lot of errors in your code. I commented out many lines to stop the errors. Having the errors keeps the test case from running to allow initComplete to execute. There is a lot of code in the test case that is not pertinent to the question. It is best to post simplified test cases to make it easier for us to debug.

    I added a check in the child's -option initComplete to see fi the row count is 0. If it is then hide the child row:

                      initComplete: function(settings, json) {
                        var api = this.api();
                        var count = api.rows().count();
                      
                        if (count === 0) {
                          row.child(table).hide();
                        }
    

    https://live.datatables.net/loborahi/6/edit

    Kevin

  • kthorngrenkthorngren Posts: 19,891Questions: 26Answers: 4,691
    edited December 2023 Answer ✓

    Since you are using ajax to load the child Datatable you might be better off hiding the td element that row().child().show() creates. Otherwise you might see a quick flash of the child row until the while the Ajax request is processing. Like this:

      // Display the child row
      row.child(table).show();
      
      $(table).closest('td').css('display', 'none');
    

    Update initComplete to display the -td and use columns.adjust():

                        if (count !== 0) {
                          $(table).closest('td').css('display', 'block');
                          api.columns.adjust();
                        }
    

    Updated example:
    https://live.datatables.net/tohoqavo/1/edit

    I added data: [{'name': 'Tiger'}], to populate the child Datatable so the child will show. Comment out this line to see how the above code behaves.

    Kevin

  • menashemenashe Posts: 121Questions: 28Answers: 1

    My apologies! Obviously, my complete code runs without errors; I thought that I was minimizing the included code! :) Again, my apologies, and thank you for looking!

    (When I paste in live.datatables, the formatting is way off. Is there a way to format?)

  • kthorngrenkthorngren Posts: 19,891Questions: 26Answers: 4,691
    edited December 2023 Answer ✓

    No problem, we can help with test case building. Just trying to point out that since we didn't build the solution it takes awhile to dig through to understand.

    When I paste in live.datatables, the formatting is way off. Is there a way to format?

    I tried fixing the formatting too but there is something off with the code structure causing the JS Bin environment to not format correctly. Usually you can highlight a section of code and use shift+tab to format the code.

    Kevin

  • menashemenashe Posts: 121Questions: 28Answers: 1
    $(table).closest('td').css('display', 'block');
    

    Only unhides the closest first (nearest) column. (See your example too.)

    How do I display the entire row?

  • allanallan Posts: 60,962Questions: 1Answers: 9,939 Site admin
    Answer ✓
    $(table).closest('td').css('display', 'block');
    

    should be:

    $(table).closest('td').css('display', 'table-cell');
    

    That allows it to span across the whole table. https://live.datatables.net/tohoqavo/4/edit

    Allan

  • menashemenashe Posts: 121Questions: 28Answers: 1

    Masterful! Is the table-cell DataTables or jQuery?

    I could not find a Googled reference to it.

  • menashemenashe Posts: 121Questions: 28Answers: 1

    In the Parent/Child Detail example on your website, it is assumed that there is always a child to destroy when "finished."

    In my example, there may not be any child(ren), so that

            function destroyChild(row) {
                let table = $("table", row.child());
                table.detach();
                table.DataTable().destroy();
    
                // And then hide the row
                row.child.hide();
            }
    
    

    fails on the first line (row.child() is invalid function).

    Yet without deleting "something", I get the message that the table cannot be reinitialized.

    I am confused. What--and how--do I need to clean up??

    Thanks!

  • kthorngrenkthorngren Posts: 19,891Questions: 26Answers: 4,691

    Masterful! Is the table-cell DataTables or jQuery?

    I've not used it either. See the W3C doc for it. Search for table-cell css and you should find some articles discussing uses.

    fails on the first line (row.child() is invalid function).

    I think you can check to see if there is a child with row().child.isShown(), for example:
    https://live.datatables.net/cuwibula/1/edit

    Yet without deleting "something", I get the message that the table cannot be reinitialized.

    Its hard to say specifically without seeing the code flow. Make sure the same table reference is not used when creating the child Datatable.

    Kevin

  • menashemenashe Posts: 121Questions: 28Answers: 1

    I think that I see the root of my issue.

    Let me please ask general question.

    For the "click" event we have:

           $('#items tbody').on('click', 'td.dt-control', function(event) {
                event.stopPropagation();
    
                let tr = $(this).closest('tr');
                let tableRow = itemsTable.row(tr);
    
                if (tableRow.child.isShown()) {
                    tr.removeClass('shown');
    
                ...
    
    

    For the "select" event we have:

            $('#items').on('select.dt', function(e, dt, type, indexes) {
                e.stopPropagation();
    
                let table = $('#items').DataTable();
    
                if (table.rows({
    
                ...
    
    

    For the select event, how can I also check child.isShown()?

  • kthorngrenkthorngren Posts: 19,891Questions: 26Answers: 4,691

    I'm not sure thats the place to put it but you will need to get the selected row, like this:

    table.row( {selected: true} ).child.isShown()
    

    Kevin

  • kthorngrenkthorngren Posts: 19,891Questions: 26Answers: 4,691
    edited December 2023

    The dt parameter of the select event gives you the Datatables API instance. So you don't need let table = $('#items').DataTable();. You can just use dt instead like this:

    $('#items').on('select.dt', function(e, dt, type, indexes) {
        e.stopPropagation();
     
        if (dt.rows({
     
        ...
    

    Also note that table.row( {selected: true} ) gets only the first selected row. If you support multiple then use rows() or make sure to force single with select.style. Otherwise you will need to loop all the table.row( {selected: true} ) to show the child for each row. Use rows().every() for this.

    Kevin

  • kthorngrenkthorngren Posts: 19,891Questions: 26Answers: 4,691
    edited December 2023

    It looks like you are trying to have multiple levels of child rows with Datatables. Are you getting the reinitialization error when opening the second level? The test case we are working with doesn't seem to have that error.

    I built this example a long time ago to show multiple child detail levels as Datatables:
    https://live.datatables.net/sawilite/1/edit

    It doesn't have the Editor part but is based on this Parent/child detail rows blog. It supports multiple levels with ajax loaded data. It can become problematic and complex to support multiple levels. Maybe you can adapt it to work for your solution. Bear in mind there are things in the demo that are specific to the demo. Like the level parameter passed to createChild(). This is used for determining the background color and which data point to show in the table. It shouldn't be needed for the solution.

    Also I noticed that id="items_hierarchy" is applied to the table tag for the child row. The ID is expected to be unique on the page. If you are using that as part of the Datatables initialization that might be why you are getting the reinitialization error as you might have multiple tables with that ID. I would recommend not using the ID in the child rows. It shouldn't be needed and, even if you made them unique, it would be difficult to keep up with.

    Kevin

  • menashemenashe Posts: 121Questions: 28Answers: 1

    Hi Kevin,

    I'm sorry for the delay! I figured it out, and neglected to post.

    I have had multiple levels for months and that works; I followed the Parent/Child Detail example on your website.

    In a nutshell, what I have now done is "co-opt" the logic from the Parent/Child Detail example to also work on a select anywhere on the row, rather than just on the row header.

    And for this select, I was neglecting to clean up--as in your example--when the user clicks the row header to collapse the child information.

    And it works!

  • kthorngrenkthorngren Posts: 19,891Questions: 26Answers: 4,691

    Good, glad its sorted out!

    Kevin

Sign In or Register to comment.