shiny DataTables footer Callback sums

不羁的心 提交于 2019-12-13 17:26:51

问题


I'm working on implementing a callback function for DataTables in a shiny app similar to this example from the DataTables forum. My thought so far from reading the DT documentation (section 4.4) was that it might be possible to apply the same class sum through the columnDefs argument of options as below, but it would also make sense if I just knew where to put the JS argument to do the classes manually like in the link.

You can delete all the columnDefs and callback arguments to see an example starting point.

app.R

library(shiny)
library(DT)

ui <- fluidPage(

  title = 'Select Table Rows',

  hr(),

  h1('A Server-side Table'),

  fluidRow(
    column(9, DT::dataTableOutput('x3'))
  )

)


server <- function(input, output, session) {

  # server-side processing
  mtcars2 = mtcars[, 1:8]
  output$x3 = DT::renderDataTable(DT::datatable(mtcars2, 
                                                extensions = 'Buttons',
                                                options = list(
                                                  scrollX = TRUE,
                                                  scrollY = TRUE,
                                                  pageLength = 10,
                                                  order = list(list(1, 'asc')),
                                                  fixedHeader = TRUE,
                                                  dom = 'Blrtip',
                                                  buttons = c('copy', 'csv', 'excel', 'pdf', 'print')
                                                #  columnDefs = JS("[
                                                #                  { className: 'sum', 'targets': [ 1,2 ] }
                                                #                  ]") 
                                                #  ),
                                                #callback =  JS(
                                                #  " function(row, data, start, end, display) {
                                                #  var api = this.api();
                                                #  
                                                #  api.columns('.sum', { page: 'current' }).every(function () {
                                                #  var sum = api
                                                #  .cells( null, this.index(), { page: 'current'} )
                                                #  .render('display')
                                                #  .reduce(function (a, b) {
                                                #  var x = parseFloat(a) || 0;
                                                #  var y = parseFloat(b) || 0;
                                                #  return x + y;
                                                #  }, 0);
                                                #  console.log(this.index() +' '+ sum); //alert(sum);
                                                #  $(this.footer()).html(sum);
                                                #  });
                                    #}"
                          )       
                )
      )
  }

shinyApp(ui = ui, server = server)

Final solution:

library(shiny)
library(DT)

ui <- fluidPage(

  title = 'Select Table Rows',

  hr(),

  h1('A Server-side Table'),

  fluidRow(
    column(9, DT::dataTableOutput('x3'))
  )

)


server <- function(input, output, session) {

  # server-side processing

  mtcars2 = mtcars[, 1:8]

  sketch <- htmltools::withTags(table(
                  class = "display",
                  style = "bootstrap",
                  tableHeader(colnames(mtcars2)),
                  tableFooter(colnames(mtcars2))
          ))

  output$x3 = DT::renderDataTable(DT::datatable(mtcars2,
                                                container = sketch,
                                                extensions = 'Buttons',
                                                options = list(
                                                  scrollX = TRUE,
                                                  scrollY = TRUE,
                                                  pageLength = 10,
                                                  order = list(list(1, 'asc')),
                                                  dom = 'Blrtip',
                                                  buttons = c('copy', 'csv', 'excel', 'pdf', 'print'),
                                                  footerCallback = JS(
       "function( tfoot, data, start, end, display ) {",
       "var api = this.api(), data;",
        "total = api.column( 1, { page: 'current'} ).data().reduce( function ( a, b ) {return a + b;} )",
        "total1 = api.column( 2, { page: 'current'} ).data().reduce( function ( a, b ) {return a + b;} )",
       "total2 = api.column( 3, { page: 'current'} ).data().reduce( function ( a, b ) {return a + b;} )",
        "total3 = api.column( 4, { page: 'current'} ).data().reduce( function ( a, b ) {return a + b;} )",
       "total4 = api.column( 5, { page: 'current'} ).data().reduce( function ( a, b ) {return a + b;} )",
        "total5 = api.column( 6, { page: 'current'} ).data().reduce( function ( a, b ) {return a + b;} )",
       "total6 = api.column( 7, { page: 'current'} ).data().reduce( function ( a, b ) {return a + b;} )",
        "total7 = api.column( 8, { page: 'current'} ).data().reduce( function ( a, b ) {return a + b;} )",
        "$( api.column( 1 ).footer() ).html(total.toFixed(2));
        $( api.column( 2 ).footer() ).html(total1.toFixed(2));
        $( api.column( 3 ).footer() ).html(total2.toFixed(2));
        $( api.column( 4 ).footer() ).html(total3.toFixed(2));
        $( api.column( 5 ).footer() ).html(total4.toFixed(2));
        $( api.column( 6 ).footer() ).html(total5.toFixed(2));
        $( api.column( 7 ).footer() ).html(total6.toFixed(2));
        $( api.column( 8 ).footer() ).html(total7.toFixed(2));",
        "}"
        ))
      ))
}

shinyApp(ui = ui, server = server)

I realize this is probably bad form for JS, however, in my case this works best so that I can apply different options to each (some currencies symbols, some averages, different decimal precisions etc.).


回答1:


In order to show the sum/total in the footer, you have to add a container to your table as done below. I also changed the JS code: the version provided below must work. Unfortunately, I cannot tell what was wrong with your JS code as I am not the javascript guy. You can play with the HTML(...) part to change the presentation of your sums.

server <- function(input, output, session) {

# server-side processing
  mtcars2 = mtcars[, 1:8]
      sketch = htmltools::withTags(table(tableFooter(c("",0,0,0,0,0,0,0,0))))
      output$x3 = DT::renderDataTable(DT::datatable(mtcars2, container = sketch,
                                                    extensions = 'Buttons',
                                                    options = list(
                                                      scrollX = TRUE,
                                                      scrollY = TRUE,
                                                      pageLength = 10,
                                                      order = list(list(1, 'asc')),
                                                      fixedHeader = TRUE,
                                                      dom = 'Blrtip',
                                                      buttons = c('copy', 'csv', 'excel', 'pdf', 'print')

                                                      footerCallback =  JS(
                                                    "function( tfoot, data, start, end, display ) {",
                                                    "var api = this.api();",
                                                    "$( api.column( 1 ).footer() ).html(",
                                                    "api.column( 1).data().reduce( function ( a, b ) {",
                                                    "return a + b;",
                                                    "} )",
                                                    ");",
                                                    "$( api.column( 2 ).footer() ).html(",
                                                    "api.column( 2 ).data().reduce( function ( a, b ) {",
                                                    "return a + b;",
                                                    "} )",
                                                    ");",
                                                    "$( api.column( 3 ).footer() ).html(",
                                                    "api.column( 3 ).data().reduce( function ( a, b ) {",
                                                    "return a + b;",
                                                    "} )",
                                                    ");",
                                                    "$( api.column( 4 ).footer() ).html(",
                                                    "api.column( 4 ).data().reduce( function ( a, b ) {",
                                                    "return a + b;",
                                                    "} )",
                                                    ");",
                                                    "$( api.column( 5 ).footer() ).html(",
                                                    "api.column( 5 ).data().reduce( function ( a, b ) {",
                                                    "return a + b;",
                                                    "} )",
                                                    ");",
                                                    "$( api.column( 6 ).footer() ).html(",
                                                    "api.column( 6 ).data().reduce( function ( a, b ) {",
                                                    "return a + b;",
                                                    "} )",
                                                    ");",
                                                    "$( api.column( 7 ).footer() ).html(",
                                                    "api.column( 7 ).data().reduce( function ( a, b ) {",
                                                    "return a + b;",
                                                    "} )",
                                                    ");",
                                                    "$( api.column( 8 ).footer() ).html(",
                                                    "api.column( 8 ).data().reduce( function ( a, b ) {",
                                                    "return a + b;",
                                                    "} )",
                                                    ");",
                                                    "}")
                                                )       
  )
  )
}



回答2:


I crafted this function as a possible solution regarding totals and formats. If you need to run a complex operation, simply run the code before and then insert values as operation = "custom". Hope it helps.

library(DT)
dat <- iris[1:4]

sketch <- htmltools::tags$table(
  tableHeader(names(dat)),
  tableFooter(rep("", ncol(dat))))

js_op <- function(column, operation, name = "", signif = 3) {

      # Auxiliar function for mean
      aux <- ifelse(
        operation == "mean", 
        paste0("map(function(num) { return num/data.length; })."), "")

      # Decimals to consider
      signif <- 10^signif

      # Operation
      if (operation %in% c("sum", "mean"))
        operation <- paste0("Math.round((a+b)*",signif,")/",signif)
      if (operation == "count")
        operation <- "data.length"
      if (operation == "custom")
        return(paste0("$(api.column(", column, ").footer()).html('", name, "')"))

      # Result
      res <- paste0(
        "$(api.column(", column, ").footer()).html('", name, "'+",
        "api.column(", column, ").data().", aux, "reduce( function ( a, b ) {",
        "return ", operation, ";",
        "} ));")  
      return(res)
    }

javascript <- JS(
      "function(tfoot, data, start, end, display ) {",
      "var api = this.api(), data;",
      js_op(0, operation = "count", name = "Counter: "),
      js_op(1, operation = "sum", name = "Sum: "),
      js_op(2, operation = "mean", name = "Mean: "),
      js_op(3, operation = "custom", name = "Something"),
      ";}")

datatable(iris[,1:4], rownames = FALSE, container = sketch, 
              options = list(footerCallback = javascript))


来源:https://stackoverflow.com/questions/42796815/shiny-datatables-footer-callback-sums

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!