简体   繁体   English

在Shiny DataTable中扩展和折叠子行

[英]Expanding and collapsing child rows in Shiny DataTable

I'm having trouble replicating a datatable object in Shiny. 我在Shiny中复制数据表对象时遇到了麻烦。 Currently, I can display what I want when I run the data table portion of the code outside of a Shiny environment. 目前,当我在Shiny环境之外运行代码的数据表部分时,我可以显示我想要的内容。 However, when I run the entire code, it's not displaying the child table. 但是,当我运行整个代码时,它不显示子表。

library(DT)
library(data.table)
library(shiny)

shinyApp(
 ui = fluidPage(DT::dataTableOutput('tbl')),
 server = function(input, output) {
 output$tbl = DT::renderDataTable(

  datatable({
    #Transform dataframe to data.table and turn the dataframe rowname into a data.table column called model
    mtcars_dt = data.table(mtcars)
    mtcars_dt[["model"]] = rownames(mtcars)
    setcolorder(mtcars_dt,c(
      which(colnames(mtcars_dt) %in% c("mpg","cyl","model")),
      which(!colnames(mtcars_dt) %in% c("mpg","cyl","model"))
    ))

    #Turn data table into a nested data.table by mpg, cyl
    mtcars_dt <- mtcars_dt[, list(cars=list(.SD)), by = list(mpg,cyl)]


    #configure datatable. Hide row number and cars columns [0,4] and enable details control on plus sign column[1]
    #turn rows into child rows and remove from parent
    cbind(' ' = '&oplus;', mtcars_dt)}, 

    escape = -2,
    options = list(
      columnDefs = list(
        list(visible = FALSE, targets = c(0,4)),
        list(orderable = FALSE, className = 'details-control', targets = 1)
      )
    ),
    callback = JS("
                  table.column(1).nodes().to$().css({cursor: 'pointer'});

                  // Format cars object into another table
                  var format = function(d) {
                  if(d != null){ 
                  var result = ('<table id=\"child_' + d[2] + '_' + d[3] + '\">').replace('.','_') + '<thead><tr>'
                  for (var col in d[4]){
                  result += '<th>' + col + '</th>'
                  }
                  result += '</tr></thead></table>'
                  return result
                  }else{
                  return '';
                  }
                  }

                  var format_datatable = function(d) {
                  var dataset = [];
                  for (i = 0; i < + d[4]['model'].length; i++) {
                  var datarow = [];
                  for (var col in d[4]){
                  datarow.push(d[4][col][i])
                  }
                  dataset.push(datarow)
                  }
                  var subtable = $(('table#child_' + d[2] + '_' + d[3]).replace('.','_')).DataTable({
                  'data': dataset,
                  'autoWidth': true, 
                  'deferRender': true, 
                  'info': false, 
                  'lengthChange': false, 
                  'ordering': true, 
                  'paging': false, 
                  'scrollX': false, 
                  'scrollY': false, 
                  'searching': false 
                  });
                  };

                  table.on('click', 'td.details-control', function() {
                  var td = $(this), row = table.row(td.closest('tr'));
                  if (row.child.isShown()) {
                  row.child.hide();
                  td.html('&oplus;');
                  } else {
                  row.child(format(row.data())).show();
                  td.html('&CircleMinus;');
                  format_datatable(row.data())
                  }
                  });")
      )
    )
  }
)

Thanks for your help! 谢谢你的帮助!

The key here seemed to be the difference between an object and an array. 这里的关键似乎是对象和数组之间的区别。 When using shiny, row.data() is an array and its fifth element is an array as well (here I clicked on the second row in the main table): 当使用有光泽时, row.data()是一个数组,它的第五个元素也是一个数组(这里我点击了主表中的第二行):

["2", "&oplus;", 22.8, 4, Array(2)]

Outside of the shiny environment, row.data() looks like this: 在闪亮的环境之外, row.data()看起来像这样:

["2", "&oplus;", 22.8, 4, Object]

As you can see, the fifth element is an object! 如你所见,第五个元素是一个对象! Why this is the case, I cannot tell. 为什么会这样,我说不出来。 I guess there might be a difference in the versions of the libraries used behind the scenes. 我猜在幕后使用的库版本可能会有所不同。

To get this working we need to make 2 changes: 为了实现这一目标,我们需要进行2项更改:

1. Change format() : 1.更改format()


var format = function(d) {
  if(d != null) { 
    var result = ('<table id=\"child_' + d[2] + '_' + d[3] + '\">').replace('.','_') + '<thead><tr>'
    for (var col in d[4][0]) {
       result += '<th>' + col + '</th>'
    }
    result += '</tr></thead></table>'
    return result
  } else {
    return '';
  }
}

Here we just added [0] in line 4. As shown above, d[4] is an array. 这里我们刚刚在第4行添加[0] 。如上所示, d[4]是一个数组。 In case of the second row of data it consists of 2 objects. 在第二行数据的情况下,它由2个对象组成。 (var col in d[4]) would return 0 and 1 (indices of the objects) whereas (var col in d[4][0]) returns the elements of the first object (so the column names). (var col in d[4])将返回01 (对象的索引),而(var col in d[4][0])返回第一个对象的元素(因此列名称)。

2. Change format_datatable() : 2.更改format_datatable()


var format_datatable = function(d) {
  var dataset = [];
  for (i = 0; i <=  d[4].length-1; i++) {
    var datarow = $.map(d[4][i], function(value, index) {
      return [value];
    });
    dataset.push(datarow);
  }
  //  ...
  //  the rest did not change
}

Here we convert each car model (so each element in d[4] ), which is represented as an object, into an array using $.map() . 在这里,我们使用$.map()将每个汽车模型(因此d[4]每个元素)(表示为对象)转换为数组。 After that we just add this array to dataset . 之后我们将这个数组添加到dataset

The code that creates the subtable expects these arrays. 创建子表的代码需要这些数组。 Data is handled differently by DataTables with respect to its type which can be checked here . DataTables根据其类型对数据进行不同的处理,可在此处进行检查。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM