Color a Collapsible(Not Visible Data Column) Row

Color a Collapsible(Not Visible Data Column) Row

zgoforthzgoforth Posts: 493Questions: 98Answers: 2
edited April 2021 in Free community support

Hello,

I changed from a tabbed data table to a table that has collapsible rows grouped by Department and then by User.Title (Name). Here is what it looks like:

For each department, their are a certain number of employees. For IT, the only people in the Department are Name #1 and Name #2, so I want to $(row).addClass('green');. If I want to do for each Department, based on the different values, how can I do that? Multiple AJAX calls?

Right now it is coloring the child row under Name #1 and Name # 2, instead of the collapsible row?

Here is my JS:

$.ajax({
                url: uri,
                method: "GET",
                headers: {
                    "Accept": "application/json; odata=verbose"
                },
                success: function(data) { // success function which will then execute "GETTING" the data to post it to a object array (data.value)
                    console.log(data);
                    if (data.d != null && data.d != undefined && data.d.results.length > 0) {
                        var table = $('#myTable').DataTable();
                        table.rows.add(data.d.results).draw();
                    }
                }
            });
         }
    
    $(document).ready(function() {
        var collapsedGroups = {};
        var top = '';
        var parent = '';
    
        var table = $('#myTable').DataTable({
        "pageLength" : 10,
        "createdRow": function( row, data, dataIndex ) {
              if ( data.MondayStatus == "P" && data.TuesdayStatus == "P" && data.WednesdayStatus == "P" && data.ThursdayStatus == "P" && data.FridayStatus == "P") {        
                $(row).addClass('green');
              }
            },
            "columns": [
                {"data": "Department",
              visible: false},
                { "data": "User.Title",
                visible: false },
                { "data": "Locations"},
                { "data": "Sunday", 
                render: function(data, type, row){
                if(type === "sort" || type === "type"){
                    return data;
                }
                return moment(data).format("MM/DD/YYYY");
            }
                },
                { "data": "Monday",
                render: function(data, type, row){
                if(type === "sort" || type === "type"){
                    return data;
                }
                return moment(data).format("MM/DD/YYYY");
            }
            },
                {"data": "MondayStatus",
                visible: false},
                { "data": "Tuesday",
                render: function(data, type, row){
                if(type === "sort" || type === "type"){
                    return data;
                }
                return moment(data).format("MM/DD/YYYY");
            }
            },
                {"data": "TuesdayStatus",
                visible: false},
                { "data": "Wednesday",
                render: function(data, type, row){
                if(type === "sort" || type === "type"){
                    return data;
                }
                return moment(data).format("MM/DD/YYYY");
            }
            },
                {"data": "WednesdayStatus",
                visible: false},
                { "data": "Thursday",
                render: function(data, type, row){
                if(type === "sort" || type === "type"){
                    return data;
                }
                return moment(data).format("MM/DD/YYYY");
            }
            },
                {"data": "ThursdayStatus",
                visible: false},
                { "data": "Friday",
                render: function(data, type, row){
                if(type === "sort" || type === "type"){
                    return data;
                }
                return moment(data).format("MM/DD/YYYY");
            }
            },
                {"data": "FridayStatus",
                visible: false}
            ],
            dom: "<'row'<'col-sm-12 col-md-10'f><'col-sm-12 col-md-2'B>>" +
                "<'row'<'col-sm-12'tr>>" +
                "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
            buttons: [{
                extend: 'collection',
                className: "btn-dark",
                text: 'Export/Update Table',
                buttons: [{
                        extend: "excel",
                        className: "btn-dark"
                    },
                    {
                        extend: "print",
                        className: "btn-dark"
                    },
                    {
                        text: 'User Guide',
                        action: function (e, dt, node, config){
                        $('#ugModal').modal('show');
            }
        },
                ],
            }],
            order: [
                [0, 'asc'],
                [1, 'asc']
            ],
            rowGroup: {
                dataSrc: [
                    'Department',
                    'User.Title'
                ],
                startRender: function(rows, group, level) {
                    var all;
                    if (level === 0) {
                        top = group;
                        all = group;
                    } else if (level === 1) {
                        parent = top + group;
                        all = parent;
                        // if parent collapsed, nothing to do
                        if (!collapsedGroups[top]) {
                            return;
                        }
                    } else {
                        // if parent collapsed, nothing to do
                        if (!collapsedGroups[parent]) {
                            return;
                        }
                        all = top + parent + group;
                    }
    
                    var collapsed = !collapsedGroups[all];
                    console.log('collapsed:', collapsed);
    
                    rows.nodes().each(function(r) {
                        r.style.display = collapsed ? 'none' : '';
                    });
                    //Add category name to the <tr>.
                    return $('<tr/>')
                        .append('<td colspan="8">' + group + ' (' + rows.count() + ')</td>')
                        .attr('data-name', all)
                        .toggleClass('collapsed', collapsed);
    
    
                }
    
            }
        });
    
        loadData();
    
        $('#myTable tbody').on('click', 'tr.dtrg-start', function() {
            var name = $(this).data('name');
            collapsedGroups[name] = !collapsedGroups[name];
            table.draw(false);
        });
    });

Answers

  • kthorngrenkthorngren Posts: 20,142Questions: 26Answers: 4,736

    If I want to do for each Department, based on the different values, how can I do that?

    If you want to style the tr displayed by rowGroup then you would do this in the rowGroupp.startRender function. You can use the group parameter or you may need to use a combination of group and level depending on what you want to want style.

    Right now it is coloring the child row under Name #1 and Name # 2, instead of the collapsible row?

    Guessing you are saying that the createdRow function is setting the color which affects the table rows. the tr returned in line 153 is where you would change the style of the rowGroup tr.

    Kevin

  • zgoforthzgoforth Posts: 493Questions: 98Answers: 2

    @kthorngren so I added it under the startRender function after it returns </tr>, and it is still targeting the very child row under name and under department.

     rows.nodes().each(function(r) {
                            r.style.display = collapsed ? 'none' : '';
                        });
                        //Add category name to the <tr>.
                        return $('<tr/>')
                            .append('<td colspan="8">' + group + ' (' + rows.count() + ')</td>')
                            .attr('data-name', all)
                            .toggleClass('collapsed', collapsed);
                            if ( data.MondayStatus == "P" && data.TuesdayStatus == "P" && data.WednesdayStatus == "P" && data.ThursdayStatus == "P" && data.FridayStatus == "P") {        
                                $(row).addClass('green');
                    }
    
  • kthorngrenkthorngren Posts: 20,142Questions: 26Answers: 4,736

    The problem with that is the function has already returned so that statement is not executed. The addClass needs to be chained with the other methods in the return statement. Lots of ways you could do this but one option is to do something like this:

                    if ( data.MondayStatus == "P" && data.TuesdayStatus == "P" && data.WednesdayStatus == "P" && data.ThursdayStatus == "P" && data.FridayStatus == "P") {       
                       return $('<tr/>')
                           .append('<td colspan="8">' + group + ' (' + rows.count() + ')</td>')
                           .attr('data-name', all)
                           .toggleClass('collapsed', collapsed)
                           .addClass('green');
                    } else {
                       return $('<tr/>')
                           .append('<td colspan="8">' + group + ' (' + rows.count() + ')</td>')
                           .attr('data-name', all)
                           .toggleClass('collapsed', collapsed);
                  }
    

    It may need some adjustment to work correctly. Like data.TuesdayStatus won't work because you are trying to access the table row data. Take a look at the docs for rowGroup.startRender to see what all the parameters are. You can loop all the rows in the group to determine if the class needs added. Or you can use the group name or you can use the level value.

    Kevin

  • zgoforthzgoforth Posts: 493Questions: 98Answers: 2

    I understand how the data values will not work in this conditional, what if I created

    var itEmployees = 2; (the only two names in the IT department)
    
    for each first child row (containing the names) if there are indeed 2 names their make the top parent green
    

    Not sure how to call the rows/groups/levels like that

  • kthorngrenkthorngren Posts: 20,142Questions: 26Answers: 4,736

    As I said the rows parameter is the rows for that group. You can access the rows data like any other array of rows using rows(). You can use rows[0] to access the first row of the group, for example.

    Kevin

  • zgoforthzgoforth Posts: 493Questions: 98Answers: 2

    I tried something like this:

    if (group == "IT" && rows.count == 2) {
                            return $('<tr/>')
                            .append('<td colspan="8">' + group + ' (' + rows.count() + ')</td>')
                            .attr('data-name', all)
                            .addclass('green')
                            .toggleClass('collapsed', collapsed);
    }
    

    and unfortunately, it doesn't populate anything to the table.

    Do I need to wrap that in a

                        for (var i = 0; i > rows.length; i++)
    
    

    and call rows[i]?

  • zgoforthzgoforth Posts: 493Questions: 98Answers: 2
    edited April 2021

    @kthorngren I have tried to create a static working example here https://jsfiddle.net/cys1g50u/3/,

    but it is grouping the items as "No group" nested with a "No group"? Why is that

  • kthorngrenkthorngren Posts: 20,142Questions: 26Answers: 4,736

    The problem with your example is you are using array based data but defined the rowGroup.dataSrc with object notation. Fixed it here:
    https://jsfiddle.net/h3po15kd/

    rows.count should be rows.count() to call the count() API.

    Kevin

  • zgoforthzgoforth Posts: 493Questions: 98Answers: 2
    for (var i = 0; i < rows.length; i++){ 
                        if (group == "IT" && rows.count() == 2){
                            return $('<tr/>')
                            .append('<td colspan="8">' + group + ' (' + rows.count() + ')</td>')
                            .attr('data-name', all)
                            .toggleClass('collapsed', collapsed)
                            .addClass('green');
                        }    
                        }
    

    So I made the adjustment in response to your response https://jsfiddle.net/nb9dp1tq/. And all this does is only populate IT the IT row, but does not add the green class nor does it allow me to expand the parent?

  • kthorngrenkthorngren Posts: 20,142Questions: 26Answers: 4,736

    Not sure why you are using the for loop. I changed your example to look like my suggestion:
    https://jsfiddle.net/foceuspa/

    The rowGroup now works properly and something is returned for the case to add the class or not. However the row is not green. So instead of using addClass("green"); for the tr you will need to add the class to the td in the row, like this:

    .append('<td class="green" colspan="8">' + group + ' (' + rows.count() + ')</td>')
    

    Kevin

  • zgoforthzgoforth Posts: 493Questions: 98Answers: 2

    @kthorngren since I can't call the data to addClass to the whole row, can I use data to color code the day of the week columns (Monday - Thursday). If MondayStatus = P (green), color the monday column green. So on and so forth for each other day of the week. And then from this, if all employees are green on monday, then to color the row with .append('<td class="green" colspan="8">' + group + ' (' + rows.count() + ')</td>')

  • kthorngrenkthorngren Posts: 20,142Questions: 26Answers: 4,736

    since I can't call the data to addClass to the whole row

    Are you wanting to look at all the rows in the group to determine the color to apply? If so you can use rows().every(), like this:
    https://jsfiddle.net/t36on5mv/

    You just need to structure the HTML you return in a way that allows you to apply the CSS to the desired columns. There are no restrictions.

    Kevin

  • zgoforthzgoforth Posts: 493Questions: 98Answers: 2

    @kthorngren so in this previous example, I am using a different way to style the rows than how I actually need to style them.

    if (group == "IT" && rows.count() == 2){
                            return $('<tr/>')
                            .append('<td colspan="8">' + group + ' (' + rows.count() + ')</td>')
                            .attr('data-name', all)
                            .toggleClass('collapsed', collapsed)
                            .addClass('green');
                        }
    

    This was how I originally was styling the parent row. But this isn't what I need to accomplish. If you see in this new fiddle, https://jsfiddle.net/0yrcwzeh/ I am using a bunch of conditionals in the "createdRow" to color each day based on what the Status is. I then want to under each group, depending on how many sub cells are colored green, style the parent row either green yellow or red.

    The fiddle isn't manipulating the static data for some reason in the child rows, so here is a screenshot of what it looks like in my actual application:

    I haven't decided if it will go day by day (only count monday on the monday, then only tuesday on the tuesday, etc. so on and so forth). But right now I am just going to go off of the whole week. How can I call like in my example above with the conditional around the return, for IT the parent row would be styled green because 80% of the colored columns are green (> 75% means parent row green, >50% & <75% ,means yellow, <50% means color the row red.)

    Am I even able to call those sub columns/rows or is that not achievable. I know I can call the group/parent variables but idk about the individual cells

  • zgoforthzgoforth Posts: 493Questions: 98Answers: 2

    I don't know why I haven't thought of this as an option either yet, so I am thinking depending on the cells that are green, appropriately style the Name row and then based off the name rows, we can then appropriately style the parent Department row.

    I have surfed this site quite a bit and have been trying to find what the correct API reference would be in which I need to utilize here. https://datatables.net/reference/api/cells().nodes()

    Is this the right direction?

  • kthorngrenkthorngren Posts: 20,142Questions: 26Answers: 4,736
    edited April 2021

    I then want to under each group, depending on how many sub cells are colored green, style the parent row either green yellow or red.

    In rowGroup.startRender you have this loop which loops through each group's api row().nodes()

                        rows.nodes().each(function(r) {
                            r.style.display = collapsed ? 'none' : '';
                        });
    

    And you have this loop which loops through each group's row(), specifically it gets the row().data().

                     rows.every(function ( rowIdx, tableLoop, rowLoop ) {
                        var data = this.data();
                        console.log(data)
                        // ... do something with data(), or this.node(), etc
                      });
    

    In either loop you can compile the data percentages to apply to the RowGroup tr.

    The fiddle isn't manipulating the static data for some reason in the child rows, so here is a screenshot of what it looks like in my actual application:

    Because your actual application uses object data but your test case uses arrays. Instead of data.MondayStatus you would use something like data[5]:

            "createdRow": function( row, data, dataIndex ) {
                    if ( data.MondayStatus == "TW" || data.MondayStatus == "P") {
                        $('td', row).eq(2).addClass( 'green' );
                    } 
    

    Kevin

  • zgoforthzgoforth Posts: 493Questions: 98Answers: 2

    @kthorngren Hello. So I was reading your response, sorry I have been extremely slammed.

    And you have this loop which loops through each group's row(), specifically it gets the row().data().

    I was confused when I read this part, because the function you mention underneath of it I am not using?

        rows.every(function ( rowIdx, tableLoop, rowLoop ) {
           var data = this.data();
           console.log(data)
           // ... do something with data(), or this.node(), etc
         });
    

    Or is that because you are telling me that is what I can implement within the >! >! rowGroup.startRender function?

    I was thinking something along the lines of

    var mData = MondayStatus.data();
    console.log(mData);
    
    if (mData.class == 'green'){
       $('tr').addClass( 'green' );
    }
    

    I know that is not exactly it, and now the more I think about it I don't think this would work. I have been stuck on this for a few days now and its starting to get under my skin haha

  • zgoforthzgoforth Posts: 493Questions: 98Answers: 2
    edited May 2021

    Hello,

    So I have been still trying to solve this and I am not having the best luck.

    Here is my test case UPDATED: https://jsfiddle.net/BeerusDev/nzxr1gL2/11/

    So I was trying to manipulate the data within the rows.every function, since I can't access the class of each DayStatus (i.e. 'green', 'yellow', 'red'). I wanted to see if I could access that so if the cells in the row are a certain color, I can do some math to determine how to color/addClass to the level 0 group.

    So it seems that all I can do is access the data in which I already used in my createdRow function? I am confused.

  • kthorngrenkthorngren Posts: 20,142Questions: 26Answers: 4,736

    So it seems that all I can do is access the data in which I already used in my createdRow function? I am confused.

    They are separate processes. The rowGroup.startRender function doesn't know anything about what you did in createdRow.

    You can use api row().node() to get the HTML if you prefer. I added this to your example to show:

                        var node = this.node();  // Get the node
                        console.log(node)
    
    

    One of the biggest problems you have is that your production code uses objects but your example uses arrays. You keep mixing the syntax which is probably the biggest hurdle you have. Instead of if(data.MondayStatus == "P"){ your example needs to use if (data[7] === 'P') { since your example is using arrays.

    You can's add the class to the group variable using group.addClass('green');. You need to add it to the tr or td that is returned at the end of startRender. Initialize a variable with an empty string before the loop. Within the loop determine the class you want and store that in the variable. Use addClass() to add that class to the tr or td.

    Simply adding the class doesn't color the tr green. That class is overridden by Datatables CSS. You need to inspect the tr to see what is applying the color, in this case it is this:

    table.dataTable tr.dtrg-group td {
        background-color: #e0e0e0;
    }
    

    You will need to use something like this in your CSS to :

    table.dataTable tr.dtrg-group.green td {
    background-color: #66b266 !important;
    }
    

    See this example:
    https://jsfiddle.net/7683vp0h/1/

    Kevin

  • zgoforthzgoforth Posts: 493Questions: 98Answers: 2
    edited May 2021

    Kevin, my apologies it is just running circles around my head switching back and forth from object to array structured data. I read over your response and tried implementing that into my Fiddle. When I try to do what the example is doing in the fiddle you linked, it posts the absolute child row and styles that how it was, but the collapsibleparent[] rows are no longer visible? https://jsfiddle.net/BeerusDev/nzxr1gL2/18/

    It gives me two errors:
    "jQuery.Deferred exception: group.addClass is not a function" &
    "Script error.". I checked my JS in JSHint and their were no errors so I am confused?

  • kthorngrenkthorngren Posts: 20,142Questions: 26Answers: 4,736

    Sorry, this line should have been commented out:
    group.addClass('green'); // This won't work. Add the class to the TR below

    As I mentioned before this won't work. See the updated

    https://jsfiddle.net/nase2uyv/

    Kevin

  • zgoforthzgoforth Posts: 493Questions: 98Answers: 2

    Kevin,

    Is the collapsible rows acting really buggy and slow for you as well? I am not sure why I am experiencing that

  • kthorngrenkthorngren Posts: 20,142Questions: 26Answers: 4,736

    Wonder if its all the console.log statements.

    Kevin

  • zgoforthzgoforth Posts: 493Questions: 98Answers: 2

    @kthorngren good point

This discussion has been closed.