【问题标题】:Downloading a Nested DT table in Shiny在 Shiny 中下载嵌套的 DT 表
【发布时间】:2020-08-16 08:27:30
【问题描述】:

我的 Shiny 应用中有一个嵌套的 DT 表。子表是一个列表,它被传递并呈现到表中。当我通过 DT 的内置方法添加下载按钮时,输出包含父表数据,然后是 [Object][Object].. 列表,而不是实际的子数据。

我是否必须编写自己的可下载按钮方法而不是使用 DT 的方法,或者是否有原始的 DT 方法可以帮助解决这个问题?还没找到解决办法。

数据

Parent
structure(list(Market = c("ABILENE, TX", "AKRON, OH"), `SQAD CPP ($)` = c(10, 
49), `SQAD CPM ($)` = c(22, 30), `Override CPP ($)` = c(0, 0), 
    `Override CPM ($)` = c(0, 0)), .Names = c("Market", "SQAD CPP ($)", 
"SQAD CPM ($)", "Override CPP ($)", "Override CPM ($)"), row.names = c(NA, 
-2L), class = "data.frame")

Child
structure(list(Market = c("ABILENE, TX", "ABILENE, TX", "ABILENE, TX", 
"ABILENE, TX", "ABILENE, TX", "ABILENE, TX", "ABILENE, TX", "ABILENE, TX", 
"ABILENE, TX", "AKRON, OH", "AKRON, OH", "AKRON, OH", "AKRON, OH", 
"AKRON, OH", "AKRON, OH", "AKRON, OH", "AKRON, OH", "AKRON, OH"
), Daypart = c(" Podcast", " Streaming/Digital Audio", "Afternoon Drive", 
"Daytime", "Evening", "Mon-Fri Average", "Mon-Sun Average", "Morning Drive", 
"Weekend", " Podcast", " Streaming/Digital Audio", "Afternoon Drive", 
"Daytime", "Evening", "Mon-Fri Average", "Mon-Sun Average", "Morning Drive", 
"Weekend"), `Mix (%)` = c(10L, 10L, 10L, 10L, 10L, 10L, 5L, 15L, 
10L, 10L, 10L, 10L, 10L, 10L, 10L, 5L, 15L, 10L), `Spot:60 (%)` = c(4, 
4, 4, 4, 4, 4, 2, 6, 4, 4, 4, 4, 4, 4, 4, 2, 6, 4), `Spot:30 (%)` = c(6, 
6, 6, 6, 6, 6, 3, 9, 6, 6, 6, 6, 6, 6, 6, 3, 9, 6), `SQAD CPP ($)` = c(10, 
6, 27, 31, 44, 32, 31, 26, 26, 34, 21, 170, 156, 112, 151, 136, 
177, 95), `SQAD CPM ($)` = c(21, 13, 57.6, 64.8, 93.6, 68.4, 
64.8, 54, 54, 21, 13, 104.5, 96.1, 69, 93, 83.6, 108.7, 58.5), 
    `Override CPP ($)` = c(10, 6, 27, 31, 44, 32, 31, 26, 26, 
    34, 21, 170, 156, 112, 151, 136, 177, 95), `Override CPM ($)` = c(21, 
    13, 57.63, 64.83, 93.64, 68.43, 64.83, 54.03, 54.03, 21, 
    13, 104.49, 96.13, 68.96, 92.99, 83.59, 108.67, 58.51), population = c(47200L, 
    47200L, 47200L, 47200L, 47200L, 47200L, 47200L, 47200L, 47200L, 
    162700L, 162700L, 162700L, 162700L, 162700L, 162700L, 162700L, 
    162700L, 162700L), slider_60s = c(0.4, 0.4, 0.4, 0.4, 0.4, 
    0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 
    0.4), slider_30s = c(0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 
    0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 0.6, 0.6)), .Names = c("Market", 
"Daypart", "Mix (%)", "Spot:60 (%)", "Spot:30 (%)", "SQAD CPP ($)", 
"SQAD CPM ($)", "Override CPP ($)", "Override CPM ($)", "population", 
"slider_60s", "slider_30s"), class = "data.frame", row.names = c(NA, 
-18L))

代码

# Bind the market level and mix breakout data together for the final table
market_mix_table <- reactive({
  # Take a dependency on input$goButton
  input$goButton

  isolate({
    markets <- market_costings_gross_net()
    mix_breakout <- mix_breakout_digital_elements()
    # Make the dataframe
    # This must be met length(children) == nrow(dat)
    Dat <- NestedData(
      dat = markets,
      children = split(mix_breakout, mix_breakout$Market)
    )
    return(Dat)
  })
})

# Render the table
output$daypartTable <- DT::renderDataTable({
  Server = FALSE
  # Whether to show row names (set TRUE or FALSE)
  rowNames <- FALSE
  colIdx <- as.integer(rowNames)
  # The data
  Dat <- market_mix_table()
  parentRows <- which(Dat[,1] != "")
  callback_js = JS(
    "var ok = true;",
    "function onUpdate(updatedCell, updatedRow, oldValue) {",
    "  var column = updatedCell.index().column;",
    "  if(column === 8){",
    "    ok = false;",
    "  }else if(column === 7){",
    "    ok = true;",
    "  }",
    "}",
    sprintf("var parentRows = [%s];", toString(parentRows-1)),
    sprintf("var j0 = %d;", colIdx),
    "var nrows = table.rows().count();",
    "for(var i=0; i < nrows; ++i){",
    "  if(parentRows.indexOf(i) > -1){",
    "    table.cell(i,j0).nodes().to$().css({cursor: 'pointer'});",
    "  }else{",
    "    table.cell(i,j0).nodes().to$().removeClass('details-control');",
    "  }",
    "}",
    "",
    "// make the table header of the nested table",
    "var format = function(d, childId){",
    "  if(d != null){",
    "    var html = ",
    "      '<table class=\"display compact hover\" ' + ",
    "      'style=\"padding-left: 30px;\" id=\"' + childId + '\"><thead><tr>';",
    "    for(var key in d[d.length-1][0]){",
    "      html += '<th>' + key + '</th>';",
    "    }",
    "    html += '</tr></thead><tfoot><tr>'",
    "    for(var key in d[d.length-1][0]){",
    "      html += '<th></th>';",
    "    }",
    "    return html + '</tr></tfoot></table>';",
    "  } else {",
    "    return '';",
    "  }",
    "};",
    "",
    "// row callback to style the rows of the child tables",
    "var rowCallback = function(row, dat, displayNum, index){",
    "  if($(row).hasClass('odd')){",
    "    $(row).css('background-color', 'white');",
    "    $(row).hover(function(){",
    "      $(this).css('background-color', 'lightgreen');",
    "    }, function() {",
    "      $(this).css('background-color', 'white');",
    "    });",
    "  } else {",
    "    $(row).css('background-color', 'white');",
    "    $(row).hover(function(){",
    "      $(this).css('background-color', 'lightblue');",
    "    }, function() {",
    "      $(this).css('background-color', 'white');",
    "    });",
    "  }",
    "};",
    "",
    "// header callback to style the header of the child tables",
    "var headerCallback = function(thead, data, start, end, display){",
    "  $('th', thead).css({",
    "    'color': 'black',",
    "    'background-color': 'white'",
    "  });",
    "};",
    "",
    "// make the datatable",
    "var format_datatable = function(d, childId, rowIdx){",
    "  // footer callback to display the totals",
    "  // and update the parent row",
    "  var footerCallback = function(tfoot, data, start, end, display){",
    "    $('th', tfoot).css('background-color', '#F5F2F2');",
    "    var api = this.api();",
    "// update the Override CPM when the Override CPP is changed",
    "    var col_override_cpp = api.column(7).data();",
    "    var col_population = api.column(9).data();",
    "    if(ok){",
    "      for(var i = 0; i < col_override_cpp.length; i++){",
    "        api.cell(i,8).data(((parseInt(col_override_cpp[i])*100)/(parseInt(col_population[i])/1000)).toFixed(0));",
    "      }",
    "    }",
    "// update the Override CPP when the Override CPM is changed",
    "    var col_override_cpm = api.column(8).data();",
    "    for(var i = 0; i < col_override_cpm.length; i++){",
    "      api.cell(i,7).data(((parseInt(col_override_cpm[i])*parseInt(col_population[i])/1000)/(100)).toFixed(0));",
    "    }",
    "// Update the spot mixes",
    "    var col_mix_percentage = api.column(2).data();",
    "    var col_mix60_mix30 = api.column(10).data();",
    "    var col_mix30_mix15 = api.column(11).data();",
    "    for(var i = 0; i < col_mix_percentage.length; i++){",
    "      api.cell(i,3).data((parseFloat(col_mix_percentage[i])*parseFloat(col_mix60_mix30[i])).toFixed(1));",
    "      api.cell(i,4).data((parseFloat(col_mix_percentage[i])*parseFloat(col_mix30_mix15[i])).toFixed(1));",
    "    }",
    "// Make the footer sums",
    "    api.columns().eq(0).each(function(index){",
    "      if(index == 0) return $(api.column(index).footer()).html('Mix Total');",
    "      var coldata = api.column(index).data();",
    "      var total = coldata",
    "          .reduce(function(a, b){return parseInt(a) + parseInt(b)}, 0);",
    "      if(index == 3 || index == 4 ||index == 5 || index == 6 || index==7 || index==8) {",
    "        $(api.column(index).footer()).html('');",
    "      } else {",
    "        $(api.column(index).footer()).html(total);",
    "      }",
    "      if(total == 100) {",
    "        $(api.column(index).footer()).css({'color': 'green'});",
    "      } else {",
    "        $(api.column(index).footer()).css({'color': 'red'});",
    "      }",
    "    })",
    "  // update the parent row",
    "    var col_share = api.column(2).data();",
    "    var col_CPP = api.column(5).data();",
    "    var col_CPM = api.column(6).data();",
    "    var col_Historical_CPP = api.column(7).data();",
    "    var col_Historical_CPM = api.column(8).data();",
    "    var CPP = 0, CPM = 0, Historical_CPP = 0, Historical_CPM = 0;",
    "    for(var i = 0; i < col_share.length; i++){",
    "      CPP += (parseInt(col_share[i])*parseInt(col_CPP[i]).toFixed(0));",
    "      CPM += (parseInt(col_share[i])*parseInt(col_CPM[i]).toFixed(0));",
    "      Historical_CPP += (parseInt(col_share[i])*parseInt(col_Historical_CPP[i]).toFixed(0));",
    "      Historical_CPM += (parseInt(col_share[i])*parseInt(col_Historical_CPM[i]).toFixed(0));",
    "    }",
    "    table.cell(rowIdx, j0+2).data((CPP/100).toFixed(2));",
    "    table.cell(rowIdx, j0+3).data((CPM/100).toFixed(2));",
    "    table.cell(rowIdx, j0+4).data((Historical_CPP/100).toFixed(2));",
    "    table.cell(rowIdx, j0+5).data((Historical_CPM/100).toFixed(2));",
    "  }",
    "  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;",
    "  if (Object.keys(d[n][0]).indexOf('_details') === -1) {",
    "    var subtable = $(id).DataTable({",
    "                 'data': dataset,",
    "                 'autoWidth': true,",
    "                 'deferRender': true,",
    "                 'info': false,",
    "                 'lengthChange': false,",
    "                 'ordering': d[n].length > 1,",
    "                 'order': [],",
    "                 'paging': true,",
    "                 'scrollX': false,",
    "                 'scrollY': false,",
    "                 'searching': false,",
    "                 'sortClasses': false,",
    "                 'pageLength': 50,",
    "                 'rowCallback': rowCallback,",
    "                 'headerCallback': headerCallback,",
    "                 'footerCallback': footerCallback,",
    "                 'columnDefs': [",
    "                  {targets: [0, 9, 10, 11], visible: false},",
    "                  {targets: '_all', className: 'dt-center'}",
    "                 ]",
    "               });",
    "  } else {",
    "    var subtable = $(id).DataTable({",
    "            'data': dataset,",
    "            'autoWidth': true,",
    "            'deferRender': true,",
    "            'info': false,",
    "            'lengthChange': false,",
    "            'ordering': d[n].length > 1,",
    "            'order': [],",
    "            'paging': true,",
    "            'scrollX': false,",
    "            'scrollY': false,",
    "            'searching': false,",
    "            'sortClasses': false,",
    "            'pageLength': 50,",
    "            'rowCallback': rowCallback,",
    "            'headerCallback': headerCallback,",
    "            'footerCallback': footerCallback,",
    "            'columnDefs': [",
    "              {targets: [0, 9, 10, 11], visible: false},",
    "              {targets: -1, visible: false},",
    "              {targets: 0, orderable: false, className: 'details-control'},",
    "              {targets: '_all', className: 'dt-center'}",
    "             ]",
    "          }).column(0).nodes().to$().css({cursor: 'pointer'});",
    "  }",
    "  subtable.MakeCellsEditable({",
    "    onUpdate: onUpdate,",
    "    inputCss: 'my-input-class',",
    "    columns: [2, 7, 8],",
    "    confirmationButton: {",
    "      confirmCss: 'my-confirm-class',",
    "      cancelCss: 'my-cancel-class'",
    "    }",
    "  });",
    "};",
    "",
    "// display the child table on click",
    "var children = [];", # array to store the id's of the already created child tables
    "table.on('click', 'td.details-control', function(){",
    "  var tbl = $(this).closest('table'),",
    "      tblId = tbl.attr('id'),",
    "      td = $(this),",
    "      row = $(tbl).DataTable().row(td.closest('tr')),",
    "      rowIdx = row.index();",
    "  if(row.child.isShown()){",
    "    row.child.hide();",
    "    td.html('<img src=\"https://raw.githubusercontent.com/DataTables/DataTables/master/examples/resources/details_open.png\"/>');",
    "  } else {",
    "    var childId = tblId + '-child-' + rowIdx;",
    "    if(children.indexOf(childId) === -1){", # this child table has not been created yet
    "      children.push(childId);",
    "      row.child(format(row.data(), childId)).show();",
    "    td.html('<img src=\"https://raw.githubusercontent.com/DataTables/DataTables/master/examples/resources/details_close.png\"/>');",
    "      format_datatable(row.data(), childId, rowIdx);",
    "    }else{",
    "      row.child(true);",
    "    td.html('<img src=\"https://raw.githubusercontent.com/DataTables/DataTables/master/examples/resources/details_close.png\"/>');",
    "    }",
    "  }",
    "});"
  )
  # Table
  table <- DT::datatable(
    Dat,
    callback = callback_js,
    rownames = rowNames,
    escape = -colIdx-1,
    style = "bootstrap4",
    extensions = 'Buttons',
      options = list(
        dom = "Blfrtip",
        buttons = list("copy",
                       list(
                          extend = "collection",
                          buttons = "csv",
                          text = "Download"
                          )
                      ),
        lengthMenu = list(c(-1, 10, 20),
                          c("All", 10, 20)),
        columnDefs = list(
          list(width = '30px', targets = 0),
          list(width = '545px', targets = 1),
          list(visible = FALSE, targets = ncol(Dat)-1+colIdx),
          list(orderable = FALSE, className = 'details-control', targets = colIdx),
          list(className = "dt-center", targets = "_all")
      )
    )
  )
  # Call the html tools deps (js & css files in this directory)
  cell_edit_dep <- htmltools::htmlDependency(
    "CellEdit", "1.0.19", 
    src = 'www/',
    script = "dataTables.cellEdit.js",
    stylesheet = "dataTables.cellEdit.css"
  )
  table$dependencies <- c(table$dependencies, list(cell_edit_dep))

  table %>% formatStyle(
    c('Market', 'SQAD CPP ($)', 'SQAD CPM ($)', 'Override CPP ($)', 'Override CPM ($)'),
    target = 'row',
    backgroundColor = "#F5F2F2"
  )
})

【问题讨论】:

    标签: r shiny dt


    【解决方案1】:

    试试这个:

    js <- c(
      "function(xlsx) {",
      "  var table = $('#daypartTable').find('table').DataTable();",
      "  // Letters for Excel columns.",
      "  var LETTERS = [",
      "    'A','B','C','D','E','F','G','H','I','J','K','L','M',",
      "    'N','O','P','Q','R','S','T','U','V','W','X','Y','Z'",
      "  ];",
      "  // Get sheet.",
      "  var sheet = xlsx.xl.worksheets['sheet1.xml'];",
      "  // Get a clone of the sheet data.        ",
      "  var sheetData = $('sheetData', sheet).clone();",
      "  // Clear the current sheet data for appending rows.",
      "  $('sheetData', sheet).empty();",
      "  // Row count in Excel sheet.",
      "  var rowCount = 1;",
      "  // Iterate each row in the sheet data.",
      "  $(sheetData).children().each(function (index) {",
      "    // Used for DT row() API to get child data.",
      "    var rowIndex = index - 2;", #
      "    // Don't process row if its the header row.",
      sprintf("    if (index > 1 && index < %d) {", nrow(Dat)+2), #
      "      // Get row",
      "      var row = $(this.outerHTML);",
      "      // Set the Excel row attr to the current Excel row count.",
      "      row.attr('r', rowCount);",
      "      // Iterate each cell in the row to change the row number.",
      "      row.children().each(function (index) {",
      "        var cell = $(this);",
      "        // Set each cell's row value.",
      "        var rc = cell.attr('r');",
      "        rc = rc.replace(/\\d+$/, \"\") + rowCount;",
      "        cell.attr('r', rc);",
      "      });",
      "      // Get the row HTML and append to sheetData.",
      "      row = row[0].outerHTML;",
      "      $('sheetData', sheet).append(row);",
      "      rowCount++;",
      "      // Get the child data - could be any data attached to the row.",
      sprintf("      var childData = table.row(':eq(' + rowIndex + ')').data()[%d];", ncol(Dat)-1), #
      "      if (childData.length > 0) {",
      "        var colNames = Object.keys(childData[0]);",
      "        // Prepare Excel formatted row",
      "        headerRow = '<row r=\"' + rowCount +",
      "          '\"><c t=\"inlineStr\" r=\"A' + rowCount +",
      "          '\"><is><t></t></is></c>';",
      "        for(var i = 0; i < colNames.length; i++){",
      "          headerRow = headerRow +",
      "            '<c t=\"inlineStr\" r=\"' + LETTERS[i+1] + rowCount +",
      "            '\" s=\"7\"><is><t>' + colNames[i] +", 
      "            '</t></is></c>';",
      "        }",
      "        headerRow = headerRow + '</row>';",
      "        // Append header row to sheetData.",
      "        $('sheetData', sheet).append(headerRow);",
      "        rowCount++; // Inc excelt row counter.",
      "      }",
      "      // The child data is an array of rows",
      "      for (c = 0; c < childData.length; c++) {",
      "        // Get row data.",
      "        child = childData[c];",
      "        // Prepare Excel formatted row",
      "        var colNames = Object.keys(child);",
      "        childRow = '<row r=\"' + rowCount +",
      "          '\"><c t=\"inlineStr\" r=\"A' + rowCount +",
      "          '\"><is><t></t></is></c>';",
      "        for(var i = 0; i < colNames.length; i++){",
      "          childRow = childRow +",
      "            '<c t=\"inlineStr\" r=\"' + LETTERS[i+1] + rowCount +",
      "            '\" s=\"5\"><is><t>' + child[colNames[i]] +", 
      "            '</t></is></c>';",
      "        }",
      "        childRow = childRow + '</row>';",
      "        // Append row to sheetData.",
      "        $('sheetData', sheet).append(childRow);",
      "        rowCount++; // Inc excel row counter.",
      "      }",
      "      // Just append the header row and increment the excel row counter.",
      "    } else {",
      "      $('sheetData', sheet).append(this.outerHTML);",
      "      rowCount++;",
      "    }",
      "  });",
      "}"
    )
    
    datatable(
      Dat, callback = callback, rownames = rowNames, escape = -colIdx-1, 
      extensions = "Buttons", 
      options = list(
        dom = "Bfrtip",
        columnDefs = list(
          list(visible = FALSE, targets = ncol(Dat)-1+colIdx),
          list(orderable = FALSE, className = 'details-control', targets = colIdx),
          list(className = "dt-center", targets = "_all")
        ), 
        buttons = list(
          list(
            extend = "excel",
            exportOptions = list(
              orthogonal = "export", 
              columns = 0:(ncol(Dat)-2)
            ),
            orientation = "landscape",
            customize = JS(js)
          )
        )
      )
    )
    

    这是生成的 Excel 文件的示例:


    编辑

    更好:

    excelTitle <- NULL # set to NULL if you don't want a title
    js <- c(
      "function(xlsx) {",
      "  var table = $('#daypartTable').find('table').DataTable();",
      "  // Number of columns.",
      "  var ncols = table.columns().count();",
      "  // Is there a title?",
      sprintf("  var title = %s;", ifelse(is.null(excelTitle), "false", "true")),
      "  // Integer to Excel column: 0 -> A, 1 -> B, ..., 25 -> Z, 26 -> AA, ...",
      "  var XLcolumn = function(j){", # https://codegolf.stackexchange.com/a/163919
      "    return j < 0 ? '' : XLcolumn(j/26-1) + String.fromCharCode(j % 26 + 65);",
      "  };",
      "  // Get sheet.",
      "  var sheet = xlsx.xl.worksheets['sheet1.xml'];",
      "  // Get a clone of the sheet data.        ",
      "  var sheetData = $('sheetData', sheet).clone();",
      "  // Clear the current sheet data for appending rows.",
      "  $('sheetData', sheet).empty();",
      "  // Row count in Excel sheet.",
      "  var rowCount = 1;",
      "  // Iterate each row in the sheet data.",
      "  $(sheetData).children().each(function (index) {",
      "    // Used for DT row() API to get child data.",
      "    var rowIndex = title ? index - 2 : index - 1;", 
      "    // Don't process row if it's the title row or the header row.",
      "    var i0 = title ? 1 : 0;",
      "    if (index > i0) {", 
      "      // Get row",
      "      var row = $(this.outerHTML);",
      "      // Set the Excel row attr to the current Excel row count.",
      "      row.attr('r', rowCount);",
      "      // Iterate each cell in the row to change the row number.",
      "      row.children().each(function (index) {",
      "        var cell = $(this);",
      "        // Set each cell's row value.",
      "        var rc = cell.attr('r');",
      "        rc = rc.replace(/\\d+$/, \"\") + rowCount;",
      "        cell.attr('r', rc);",
      "      });",
      "      // Get the row HTML and append to sheetData.",
      "      row = row[0].outerHTML;",
      "      $('sheetData', sheet).append(row);",
      "      rowCount++;",
      "      // Get the child data - could be any data attached to the row.",
      "      var childData = table.row(':eq(' + rowIndex + ')').data()[ncols-1];", 
      "      if (childData.length > 0) {",
      "        var colNames = Object.keys(childData[0]);",
      "        // Prepare Excel formatted row",
      "        var headerRow = '<row r=\"' + rowCount +",
      "          '\"><c t=\"inlineStr\" r=\"A' + rowCount +",
      "          '\"><is><t></t></is></c>';",
      "        for(let i = 0; i < colNames.length; i++){",
      "          headerRow = headerRow +",
      "            '<c t=\"inlineStr\" r=\"' + XLcolumn(i+1) + rowCount +",
      "            '\" s=\"7\"><is><t>' + colNames[i] +", 
      "            '</t></is></c>';",
      "        }",
      "        headerRow = headerRow + '</row>';",
      "        // Append header row to sheetData.",
      "        $('sheetData', sheet).append(headerRow);",
      "        rowCount++; // Inc excel row counter.",
      "      }",
      "      // The child data is an array of rows",
      "      for(let c = 0; c < childData.length; c++){",
      "        // Get row data.",
      "        var child = childData[c];",
      "        // Prepare Excel formatted row",
      "        var childRow = '<row r=\"' + rowCount +",
      "          '\"><c t=\"inlineStr\" r=\"A' + rowCount +",
      "          '\"><is><t></t></is></c>';",
      "        var i = 0;",
      "        for(let colname in child){",
      "          childRow = childRow +",
      "            '<c t=\"inlineStr\" r=\"' + XLcolumn(i+1) + rowCount +",
      "            '\" s=\"5\"><is><t>' + child[colname] +", 
      "            '</t></is></c>';",
      "          i++;",
      "        }",
      "        childRow = childRow + '</row>';",
      "        // Append row to sheetData.",
      "        $('sheetData', sheet).append(childRow);",
      "        rowCount++; // Inc excel row counter.",
      "      }",
      "      // Just append the header row and increment the excel row counter.",
      "    } else {",
      "      $('sheetData', sheet).append(this.outerHTML);",
      "      rowCount++;",
      "    }",
      "  });",
      "}"
    )
    
    datatable(
      Dat, callback = callback, rownames = rowNames, escape = -colIdx-1, 
      extensions = "Buttons", 
      options = list(
        dom = "Bfrtip",
        columnDefs = list(
          list(visible = FALSE, targets = ncol(Dat)-1+colIdx),
          list(orderable = FALSE, className = 'details-control', targets = colIdx),
          list(className = "dt-center", targets = "_all")
        ), 
        buttons = list(
          list(
            extend = "excel",
            exportOptions = list(
              orthogonal = "export", 
              columns = 0:(ncol(Dat)-2)
            ),
            title = excelTitle,
            orientation = "landscape",
            customize = JS(js)
          )
        )
      )
    )
    

    编辑 2

    callback_js,替换

    "  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;",
    "  if (Object.keys(d[n][0]).indexOf('_details') === -1) {",
    "    var subtable = $(id).DataTable({",
    "                 'data': dataset,",
    "                 'autoWidth': true,",
    "                 'deferRender': true,",
    "                 'info': false,",
    "                 'lengthChange': false,",
    "                 'ordering': d[n].length > 1,",
    "                 'order': [],",
    "                 'paging': true,",
    "                 'scrollX': false,",
    "                 'scrollY': false,",
    "                 'searching': false,",
    "                 'sortClasses': false,",
    "                 'pageLength': 50,",
    "                 'rowCallback': rowCallback,",
    "                 'headerCallback': headerCallback,",
    "                 'footerCallback': footerCallback,",
    "                 'columnDefs': [",
    "                  {targets: [0, 9, 10, 11], visible: false},",
    "                  {targets: '_all', className: 'dt-center'}",
    "                 ]",
    "               });",
    "  } else {",
    "    var subtable = $(id).DataTable({",
    "            'data': dataset,",
    "            'autoWidth': true,",
    "            'deferRender': true,",
    "            'info': false,",
    "            'lengthChange': false,",
    "            'ordering': d[n].length > 1,",
    "            'order': [],",
    "            'paging': true,",
    "            'scrollX': false,",
    "            'scrollY': false,",
    "            'searching': false,",
    "            'sortClasses': false,",
    "            'pageLength': 50,",
    "            'rowCallback': rowCallback,",
    "            'headerCallback': headerCallback,",
    "            'footerCallback': footerCallback,",
    "            'columnDefs': [",
    "              {targets: [0, 9, 10, 11], visible: false},",
    "              {targets: -1, visible: false},",
    "              {targets: 0, orderable: false, className: 'details-control'},",
    "              {targets: '_all', className: 'dt-center'}",
    "             ]",
    "          }).column(0).nodes().to$().css({cursor: 'pointer'});",
    "  }",
    

    "  var n = d.length - 1;",
    "  var id = 'table#' + childId;",
    "  var columns = Object.keys(d[n][0]).map(function(x){",
    "    return {data: x, title: x};",
    "  });",
    "  if (Object.keys(d[n][0]).indexOf('_details') === -1) {",
    "    var subtable = $(id).DataTable({",
    "                 'data': d[n],",
    "                 'columns': columns,",
    "                 'autoWidth': true,",
    "                 'deferRender': true,",
    "                 'info': false,",
    "                 'lengthChange': false,",
    "                 'ordering': d[n].length > 1,",
    "                 'order': [],",
    "                 'paging': true,",
    "                 'scrollX': false,",
    "                 'scrollY': false,",
    "                 'searching': false,",
    "                 'sortClasses': false,",
    "                 'pageLength': 50,",
    "                 'rowCallback': rowCallback,",
    "                 'headerCallback': headerCallback,",
    "                 'footerCallback': footerCallback,",
    "                 'columnDefs': [",
    "                  {targets: [0, 9, 10, 11], visible: false},",
    "                  {targets: '_all', className: 'dt-center'}",
    "                 ]",
    "               });",
    "  } else {",
    "    var subtable = $(id).DataTable({",
    "            'data': d[n],",
    "            'columns': columns,",
    "            'autoWidth': true,",
    "            'deferRender': true,",
    "            'info': false,",
    "            'lengthChange': false,",
    "            'ordering': d[n].length > 1,",
    "            'order': [],",
    "            'paging': true,",
    "            'scrollX': false,",
    "            'scrollY': false,",
    "            'searching': false,",
    "            'sortClasses': false,",
    "            'pageLength': 50,",
    "            'rowCallback': rowCallback,",
    "            'headerCallback': headerCallback,",
    "            'footerCallback': footerCallback,",
    "            'columnDefs': [",
    "              {targets: [0, 9, 10, 11], visible: false},",
    "              {targets: -1, visible: false},",
    "              {targets: 0, orderable: false, className: 'details-control'},",
    "              {targets: '_all', className: 'dt-center'}",
    "             ]",
    "          }).column(0).nodes().to$().css({cursor: 'pointer'});",
    "  }",
    

    此外,您可能不希望 Excel 文件中的隐藏列。所以替换这段代码:

      "      if (childData.length > 0) {",
      "        var colNames = Object.keys(childData[0]);",
      "        // Prepare Excel formatted row",
      ......
      "        // Append row to sheetData.",
      "        $('sheetData', sheet).append(childRow);",
      "        rowCount++; // Inc excel row counter.",
      "      }",
    

      "      if (childData.length > 0) {",
      "        var colNames = Object.keys(childData[0]).slice(1,9);",
      "        // Prepare Excel formatted row",
      "        var headerRow = '<row r=\"' + rowCount +",
      "          '\"><c t=\"inlineStr\" r=\"A' + rowCount +",
      "          '\"><is><t></t></is></c>';",
      "        for(let i = 0; i < colNames.length; i++){",
      "          headerRow = headerRow +",
      "            '<c t=\"inlineStr\" r=\"' + XLcolumn(i+1) + rowCount +",
      "            '\" s=\"7\"><is><t>' + colNames[i] +", 
      "            '</t></is></c>';",
      "        }",
      "        headerRow = headerRow + '</row>';",
      "        // Append header row to sheetData.",
      "        $('sheetData', sheet).append(headerRow);",
      "        rowCount++; // Inc excel row counter.",
      "      }",
      "      // The child data is an array of rows",
      "      for(let c = 0; c < childData.length; c++){",
      "        // Get row data.",
      "        var child = childData[c];",
      "        // Prepare Excel formatted row",
      "        var childRow = '<row r=\"' + rowCount +",
      "          '\"><c t=\"inlineStr\" r=\"A' + rowCount +",
      "          '\"><is><t></t></is></c>';",
      "        for(let i = 0; i < colNames.length; i++){",
      "          childRow = childRow +",
      "            '<c t=\"inlineStr\" r=\"' + XLcolumn(i+1) + rowCount +",
      "            '\" s=\"5\"><is><t>' + child[colNames[i]] +", 
      "            '</t></is></c>';",
      "        }",
      "        childRow = childRow + '</row>';",
      "        // Append row to sheetData.",
      "        $('sheetData', sheet).append(childRow);",
      "        rowCount++; // Inc excel row counter.",
      "      }",
    

    编辑 3

    如果子表的列名中有一些句点,这将不起作用。这是修复:

      "  var columns = Object.keys(d[n][0]).map(function(x){",
      "    return {data: x.replace('.', '\\\\\\.'), title: x};",
      "  });",
    

    【讨论】:

    • 两种用例都有效。但是,如果我要编辑子表编号,则这些编辑不会反映在下载中。下载包含他们的原始号码。我假设我们只需要调整 JS 代码??
    • @TimothyMcwilliams 嗯...如果在DT::renderDataTable 的右花括号后添加server = FALSE 是否有效:}, server = FALSE)
    • 不,添加 }, server = FALSE) 并没有帮助,这很有趣
    • 两种解决方案都按预期工作!!再次非常感谢
    • 你好。上述解决方案中的slice()sn-p 出于某种原因破坏了导出的下载。任何想法为什么??
    猜你喜欢
    • 2021-06-22
    • 2021-11-20
    • 2020-05-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-08
    • 2018-10-08
    相关资源
    最近更新 更多