How to conditionally implement child rows

How to conditionally implement child rows

TomBajzekTomBajzek Posts: 164Questions: 37Answers: 1

I have a table where some columns are optionally filled only in some modest fraction of the rows. I'd like to display the table, which is quite wide, with one line per table row, using a child row to display the optional columns, and providing the column-detail icon for displaying the child row only when the optional columns in the table row actually contain data. I'm trying to model this on the Child-row example in DataTables. This means that I need to conditionally set the className of the column based on the data of the optional columns in that row, so that the icon for displaying the child-row only appears if that child row would have actual contents.

It seems that I would need to add 'details-control' to the className for that column, based on the data in the, row's optional columns. This would look roughly like this:

        columns: [
            {
                "className": function () {
                    if (  other_column_data !=='' ) {
                        return " details-control";
                    } else {
                        return '';
                    }
                },
                "orderable": false,
                "data": null,
                "defaultContent": ''
            },   ...

My problem is that I haven't figured out how to refer to the other_column_data in my 4th line above. How can I do this?

Alternatively, is there another (better) way to accomplish what I want to do here?

This question has an accepted answers - jump to answer

Answers

  • kthorngrenkthorngren Posts: 21,330Questions: 26Answers: 4,951

    This example should help. It uses rowCallback to remove the details-control if data doesn't exist in the child.
    http://live.datatables.net/pifugoki/1/edit

    Kevin

  • TomBajzekTomBajzek Posts: 164Questions: 37Answers: 1

    Kevin,

    Thanks for the help. I did not know about the createdRow callback. I did not use the rowCallback option.

    However, you might want to note that your example did not work for me until I changed the test for null as shown below:

              createdRow: function ( row, data, index ) {
                if (data.extn === null) {
                  var td = $(row).find("td:first");
                  td.removeClass( 'details-control' );
                }
               },
    

    Thanks,
    Tom

  • kthorngrenkthorngren Posts: 21,330Questions: 26Answers: 4,951
    Answer ✓

    I did not know about the createdRow callback. I did not use the rowCallback option.

    Sorry, yes my example uses createdRow not rowCallback. Glad it worked for you.

    Kevin

  • jamiedewitzjamiedewitz Posts: 29Questions: 5Answers: 0

    Is it possible to use this approach when dealing with razor? It is not working for me, unless I missed something...

    This is what I have:

    @Html.HiddenFor(modelItem => item.Person, new { @id = "PersonHiddenField" })
    
    $(document).ready(function () {
                var person = document.getElementById("PersonHiddenField").value;
    
                // DataTable
                $('#directoryDataTable').DataTable({             
                    responsive: {
                        details: {
                            renderer: function (api, rowIdx, columns) {
                                var data = $.map(columns, function (col, i) {
                                    return col.hidden ?
                                        '<tr data-dt-row="' + col.rowIndex + '" data-dt-column="' + col.columnIndex + '">' +
                                        '<td class="col-xs-3">' + col.title + ':' + '</td> ' +
                                        '<td class="col-xs-3">' + col.data + '</td>' +
                                        '</tr>' :
                                        '';
                                }).join('');
    
                                return data ? $('<table class="table-striped col-xs-6"/>').append(data) : false;
                            }
                        },
                    },
                    createdRow: function (row, data, index) {
                        if (person === 'False') { /* value of item.Person checkbox */
                            var td = $(row).find("td:first");
                            td.removeClass('details-control');
                        }
                    }
                });
            });
    
  • kthorngrenkthorngren Posts: 21,330Questions: 26Answers: 4,951
    edited November 2019

    @jamiedewitz I'm not familiar with Razor but don't think there is anything special about removing the details-control class. When you say its not working do you mean the plus sign image is not removed when expected?

    I suspect the problem is with if (person === 'False'). What is person? Where is it defined? Just above the if statement use console.log(person) to see what the value is.

    Kevin

  • jamiedewitzjamiedewitz Posts: 29Questions: 5Answers: 0

    Yes, sorry, I meant the plus sign is not being removed on the rows where Person equals false. Person is defined in the model as a boolean value.

    It looks like the table data isn't getting refreshed when I click on next, so Person ends up being true because the first page of results is only listing actual people that are marked as true. Page 122 has a value where Person equals false, and it shows that when I am inspecting the page, but the javascript is not getting the value.

    I know I have seen a way to refresh the table data in datatables when a filter gets updated, but I haven't seen a way that will work with client-side which is what I use. Do you have an idea?

  • kthorngrenkthorngren Posts: 21,330Questions: 26Answers: 4,951

    Is person a global variable or part of each row's data?

    If its part of each row's data then you will want something like this:

                    createdRow: function (row, data, index) {
                        if (data.person === 'False') { /* value of item.Person checkbox */
                            var td = $(row).find("td:first");
                            td.removeClass('details-control');
                        }
                    }
    

    Page 122 has a value where Person equals false, and it shows that when I am inspecting the page, but the javascript is not getting the value.

    How are you updating the pages?

    Sorry, I'm not clear on what you are doing. Can you post a link to your page or a test case so we can see what you have?
    https://datatables.net/manual/tech-notes/10#How-to-provide-a-test-case

    Kevin

  • jamiedewitzjamiedewitz Posts: 29Questions: 5Answers: 0
    edited November 2019

    Hi Kevin, I added data.person === 'False' but something just isn't triggering the row to remove the plus sign class. I hope this sample datatable of mine isn't too slow to load this time, I've tried to remove everything that isn't needed for the sample to run. It looks like the console.log(person) is showing as True for both listings, but you can see in the html that the PersonHiddenField value of the second row is equal to False, so I don't know what I am doing wrong when I am trying to get the value of that field.

    http://live.datatables.net/yomuboko/1/edit?html,js,output

  • kthorngrenkthorngren Posts: 21,330Questions: 26Answers: 4,951

    Thanks for the test case. You are using this:

    var person = document.getElementById("PersonHiddenField").value; /* get value of person hidden field */
    

    The problem is that you have multiple ID's with the same value PersonHiddenField on the page. The ID's need to be unique on the page. You are getting this value once and it is retrieving the first occurrence.

    You will need to retrieve the PersonHiddenField inout value in the createdRow function. I updated your test case to show this:
    http://live.datatables.net/xituguyu/1/edit

    Once I got this working I realized you are using Responsive. This is different than the Child Detail Rows that this thread is talking about.

    I'm not sure how to remove the Responsive plus icon. Also the responsive click event would need to be removed from these cells. If there is a way @allan or @colin might be able to guide the way.

    Kevin

  • jamiedewitzjamiedewitz Posts: 29Questions: 5Answers: 0

    Oh oops, sorry for piggybacking on a discussion that didn't quite fit my issue! Thanks for all the help though, I appreciate it!

  • allanallan Posts: 63,498Questions: 1Answers: 10,471 Site admin

    You could use:

    table.dataTable.dtr-inline.collapsed > tbody > tr[role="row"] > td:first-child::before {
      display: none !important;
    }
    

    to remove the icon. Putting that into another class and then conditionally assigning the class would perhaps be the way to do it in this case, but Responsive wasn't really designed with that in mind. It either collapses all rows or none.

    Allan

This discussion has been closed.