请求协助实现R Shiny中带两层子行的NestedTable嵌套表格
Hey there! Let's fix your nested DT table issue so you can get that two-level expandable functionality working properly. Your current code only handles one level of expansion, and there are a few syntax issues in the JS callback that are holding it back. Here's the revised code with explanations of the key changes:
library(shiny) library(shinydashboard) library(tidyverse) library(DT) library(data.table) ui <- fluidPage(fluidRow(DT::dataTableOutput(width = "100%", "table"))) server <- function(input, output) { output$table = DT::renderDataTable({ # Sample data (fixed row length mismatch) DATA <- data.table(Team = c(3,3, 4, 4, 39, 41, 43), Workdone = c(2,2, 1, 1, 1, 1, 1), Q1 = c(0,0, 0, 0, 0, 0, 0), Q2 = c(1, 1, 1, 1,1,1,1), Q3 = c(0,0, 0, 0, 0, 0,0), Name = c("A", "B", "C", "D", "E", "F", "G"), Number = c(1, 1, 1, 1,1,1,1), q1 = c(0,0,0,0,0,0,0), q2 = c(100, 100, 100, 100, 100, 100, 100), q3 = c(0,0,0,0,0,0,0), Data = c("2020","2021", "2011", "2021", "2011", "2015", "2022"), Type = c("Normal","Normal","Normal","Normal","Normal","Normal","Normal"), ID = c(0,0, 0, 0, 0, 0,0), ID2 = c("A", "B", "C", "D", "E", "F", "G"), Channel = c(1, 1, 1, 1,1,1,1), Topic1 = c(0,0,0,0,0,0,0), Topic2 = c(100, 100, 100, 100, 100, 100, 100), Topic3 = c(0,0,0,0,0,0,0)) # Create third-level data (child of child) DATA3 <- DATA[, c("Topic1", "Topic2", "Topic3", "ID2", "Channel")] DATA3 <- DATA3[, list(sub_data = list(.SD)), by = list(ID2, Channel)] DATA3 <- cbind(' ' = '►', DATA3) # Create second-level data (child of parent) DATA2 <- DATA[, c(6:17)] DATA2 <- DATA2[, list(cars = list(DATA3)), by = list(Name, Number, q1, q2, q3)] DATA2 <- cbind(' ' = '►', DATA2) # Create parent-level data DATA <- DATA[, list(cars = list(DATA2)), by = list(Team, Workdone, Q1, Q2, Q3)] DATA <- cbind(' ' = '►', DATA) callback <- DT::JS( "// Handle both parent and child table clicks", "var childId = function(d, level) {", " var tail = d.slice(2, d.length - 1);", " return 'child_' + level + '_' + tail.join('_').replace(/[\\s|\\.|'|,|\\(|\\)]/g, '_');", "};", "", "var format = function(d, level) {", " if (d != null) {", " var id = childId(d, level);", " var html = '<table class=\"display compact\" id=\"' + id + '\"><thead><tr>';", " for (var key in d[d.length-1][0]) {", " html += '<th>' + key + '</th>';", " }", " html += '</tr></thead></table>';", " return html;", " } else {", " return '';", " }", "};", "", "var rowCallback = function(row, dat, displayNum, index) {", " if($(row).hasClass('odd')) {", " $('td', row).css('background-color', 'white');", " } else {", " $('td', row).css('background-color', 'white');", " }", "};", "", "var headerCallback = function(thead, data, start, end, display) {", " $('th', thead).css({", " 'border-top': '2px solid black',", " 'color': 'black',", " 'background-color': '#ffffff'", " });", "};", "", "var format_datatable = function(d, level) {", " var dataset = [];", " var n = d.length - 1;", " for (var i = 0; i < d[n].length; i++) {", " var datarow = $.map(d[n][i], function(value, index) {", " return [value];", " });", " dataset.push(datarow);", " }", " var id = 'table#' + childId(d, level);", " var subtable = $(id).DataTable({", " 'data': dataset,", " 'autoWidth': true,", " 'deferRender': true,", " 'info': false,", " 'lengthChange': false,", " 'ordering': d[n].length > 1,", " 'order': [],", " 'paging': false,", " 'scrollX': false,", " 'scrollY': false,", " 'searching': false,", " 'sortClasses': false,", " 'rowCallback': rowCallback,", " 'headerCallback': headerCallback,", " 'columnDefs': [", " {targets: 0, className: 'details-control', orderable: false},", " {targets: 3, width: '50px'},", " {targets: 4, width: '50px'},", " {targets: 5, width: '50px'},", " {targets: 1, width: '100px'},", " {targets: 2, width: '75px'},", " {targets: 3, width: '60px'},", " {targets: 1, className: 'dt-right'}", " ]", " });", "", " // Add click handler for second-level tables", " if (level === 1) {", " subtable.on('click', 'td.details-control', function() {", " var td = $(this);", " var row = subtable.row(td.closest('tr'));", " if (row.child.isShown()) {", " row.child.hide();", " td.html('►');", " } else {", " row.child(format(row.data(), 2)).show();", " td.html('⊖');", " format_datatable(row.data(), 2);", " }", " });", " }", "};", "", "// Initialize parent table click handler", "table.column(1).nodes().to$().css({cursor: 'pointer'});", "table.on('click', 'td.details-control', function() {", " var td = $(this);", " var row = table.row(td.closest('tr'));", " if (row.child.isShown()) {", " row.child.hide();", " td.html('►');", " } else {", " row.child(format(row.data(), 1)).show();", " td.html('⊖');", " format_datatable(row.data(), 1);", " }", "});" ) datatable( DATA, escape = FALSE, # Allow HTML buttons to render options = list( searching = FALSE, dom = 't', pageLength = 30, columnDefs = list( list(className = 'details-center', targets = c(2:6)), list(orderable = FALSE, className = 'details-control', targets = 1) ) ), callback = DT::JS(callback) ) }, server = FALSE) } shinyApp(ui = ui, server = server)
Key Fixes & Improvements:
- Three-Level Data Nesting: Created
DATA3as the deepest level of data, nested it insideDATA2(first child), then nestedDATA2inside the parentDATAto support two levels of expansion. - Unique Child Table IDs: Added a
levelparameter to thechildIdfunction to prevent DOM conflicts between parent and child tables. - Dual-Level Click Handling: Extended the JS callback to handle clicks on both parent and first-child tables, triggering expansion for the next level each time.
- Fixed Data & Syntax Issues: Corrected row length mismatches in sample data and fixed missing commas in JS
columnDefsthat caused silent failures. - Proper Button Rendering: Set
escape = FALSEso expand/collapse buttons render as clickable icons instead of raw HTML text. - Consistent Styling: Applied uniform row and header styling across all table levels for a polished look.
Now when you run this code, clicking the expand button on a parent row will show the first-level child table, and clicking the expand button on any child row will reveal the second-level sub-table. Collapsing works in reverse too.
内容的提问来源于stack exchange,提问作者Omer
相关产品推荐
相关产品推荐

