How To Add totals to a DT::datatable? - javascript
I am trying to add totals to a data table footer. Using code from different sources, I wrote the following application using Shiny. The problem is, when I run it, the following message appears:
"Processing ..."
and stays there forever.
My guess is the JS() code, but cannot debug that.
library(shiny)
library(DT)
library(htmltools)
ui <- fluidPage(
fluidRow(
column(9, DT::dataTableOutput('withtotal'))
)
)
server <- function(input, output, session) {
# server-side processing
mtcars2 = mtcars[, 1:8]
#sketch <- htmltools::withTags(table(tableHeader(mtcars2), tableFooter(mtcars2)))
sketch = htmltools::withTags(table(tableFooter(c("",0,0,0,0,0,0,0))))
opts <- list( footerCallback = JS("function ( row, data, start, end, display ) {",
"var api = this.api();",
"var intVal = function ( i ) {",
"return typeof i === 'string' ?",
"i.replace(/[\\$,]/g, '')*1 :",
"typeof i === 'number' ?",
"i : 0;",
"};",
"if (api.column(COLNUMBER).data().length){",
"var total = api",
".column( COLNUMBER )",
".data()",
".reduce( function (a, b) {",
"return intVal(a) + intVal(b);",
"} ) }",
"else{ total = 0};",
"if (api.column(COLNUMBER).data().length){",
"var pageTotal = api",
".column( COLNUMBER, { page: 'current'} )",
".data()",
".reduce( function (a, b) {",
" return intVal(a) + intVal(b);",
"} ) }",
"else{ pageTotal = 0};",
"$( api.column(COLNUMBER).footer() ).html(",
"'$'+pageTotal",
");",
"}"))
output$withtotal = DT::renderDataTable(DT::datatable(mtcars2,container = sketch, options = opts))
}
options(shiny.error = browser)
# Run the application
shinyApp(ui = ui, server = server)
This is a version without using Shiny. I used the same JavaScript as #gscott above, but this is for a standalone table. I used this in a RMarkdown document. The key to this, which I struggled with, is the container argument, which adds the footer to the table.
library(htmlwidgets)
library(DT)
library(htmltools)
sketch <- htmltools::withTags(table(
tableHeader(colnames(mtcars)),
tableFooter(c(0,0,0,0,0,0,0,0,0,0,0,0))
))
jsCode <- "function(row, data, start, end, display) {
var api = this.api(), data;
total = api.column(7, {page: 'current'}).data().reduce( function(a, b) { return a +
b}, 0);
total2 = api.column(6, {page: 'current'}).data().reduce( function(a, b) { return a
+ b}, 0);
total3 = api.column(2, {page: 'current'}).data().reduce( function(a, b) { return a
+ b}, 0);
$( api.column(7).footer() ).html('Total: ' + total.toFixed(2));
$( api.column(6).footer() ).html('Total: ' + total2.toFixed(2));
$( api.column(2).footer() ).html('Total: ' + total3.toFixed(2))
}"
DT::datatable(mtcars, container = sketch, options=list(scrollY=300, scrollX=TRUE, scroller=TRUE, footerCallback = JS(jsCode)))
Looks like you're using the same example program I did in shiny DataTables footer Callback sums
so my solution to the footer callback issue is below. This is the easiest that I've found to manipulate on a column by column basis.
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 managed to solve the same problem with the answers above, and the following is my implementation to reduce the amount of repeated JavaScript codes.
I looped through the columns using columns().eq(0).each(), then aggregate the data, and add the result to the footer.
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) {
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;",
"api.columns().eq(0).each( function(index) {",
"var col = api.column(index);",
"if(index == 0) return $(api.column(index).footer()).html('Total')",
"var data = col.data();",
"total = data.reduce( function(a, b) { return a + b }, 0 );",
"$( api.column(index).footer() ).html(total.toFixed(2));",
"})",
"}"
))
))
}
Related
How to run R code from inside a JS section of code?
I have some experience with R by now but very little knowledge of JS. The below reproducible code uses JS to run the package jsTreeR so the user can custom build a hierarchy tree. I would like to replace the JS line in the reproducible code flagged with comment // HELP!! (about halfway down the code, inside the script <- function) with this bit of R code, for generating a sequential list of capital letters from A to ZZ: c(LETTERS, sapply(LETTERS, function(x) paste0(x, LETTERS))) Any ideas how to do this? The code allows the user to drag/drop elements from the "Menu" section of the hierarchy tree to the "Drag here to build tree" section beneath, with the structure reflected in the dataframe to the right. I did find some related questions online but they were extremely outdated. Maybe things have improved since then, such as a nifty package that translates from R to JS. Who knows. Reproducible code: library(jsTreeR) library(shiny) nodes <- list( list( text = "Menu", state = list(opened = TRUE), children = list( list(text = "Bog",type = "moveable"), list(text = "Hog",type = "moveable") ) ), list( text = "Drag here to build tree", type = "target", state = list(opened = TRUE) ) ) checkCallback <- JS(" function(operation, node, parent, position, more) { if(operation === 'copy_node') { var n = parent.children.length; if(position !== n || parent.id === '#' || node.parent !== 'j1_1' || parent.type !== 'target') { return false; } } if(operation === 'delete_node') { if (node.type == 'item'){ text = node.text; Shiny.setInputValue('deletion', text, {priority: 'event'}); } else if (node.type == 'subitem'){ text = parent.text; Shiny.setInputValue('deletionsub', text, {priority: 'event'}); } } return true; }" ) customMenu <- JS(" function customMenu(node) { var tree = $('#mytree').jstree(true); var items = { 'delete' : { 'label' : 'Delete', 'action' : function (obj) { parent = tree.get_node(node.parent); nodetype = node.type; orgid = node.orgid; tree.delete_node(node); }, 'icon' : 'fa fa-trash' }, }; if (node.type == 'item') {return {'delete':items.delete}} else return {} } " ) dnd <- list( always_copy = TRUE, inside_pos = "last", is_draggable = JS( "function(node) {", " return node[0].type === 'moveable';", "}" ) ) mytree <- jstree( nodes, dragAndDrop = TRUE, dnd = dnd, checkCallback = checkCallback, contextMenu = list(items = customMenu), types = list(moveable = list(), target = list()) ) script <- ' var LETTERS = ["A", "B", "C", "D", "E", "F"]; // HELP!! var Visited = {}; function getSuffix(orgid){ if (Object.keys(Visited).indexOf(orgid) === -1){Visited[orgid] = 0;} else{Visited[orgid]++;} return LETTERS[Visited[orgid]]; } $(document).ready(function(){ $("#mytree").on("copy_node.jstree", function(e, data){ var orgid = data.original.id; var node = data.node; var id = node.id; var basename= node.text; var text = basename + " " + getSuffix(orgid); Shiny.setInputValue("choice", text, {priority: "event"}); var instance = data.new_instance; instance.rename_node(node, text); node.type = "item" node.basename = basename; node.orgid = orgid; var tree = $("#mytree").jstree(true); }); }); ' ui <- fluidPage( tags$div(class = "header", checked = NA,tags$p(tags$script(HTML(script)))), fluidRow( column(width = 4,jstreeOutput("mytree")), column(width = 8,verbatimTextOutput("choices")) ) ) server <- function(input, output, session){ output[["mytree"]] <- renderJstree(mytree) Choices <- reactiveVal(data.frame(choice = character(0))) observeEvent(input[["choice"]], {Choices(rbind(Choices(), data.frame(choice = input[["choice"]])))} ) observeEvent(input[["deletion"]], { item = input[["deletion"]] matched = which(Choices()$choice == item) if (length(matched)>0) Choices(Choices()[-matched, , drop = FALSE]) }) output[["choices"]] <- renderPrint({Choices()}) } shinyApp(ui=ui, server=server)
Starting with a portion of your script (just for demonstration, you use the full thing): script <- ' var Visited = {}; function getSuffix(orgid){ if (Object.keys(Visited).indexOf(orgid) === -1){Visited[orgid] = 0;} else{Visited[orgid]++;} return LETTERS[Visited[orgid]]; }' We can do this: script <- paste("var LETTERS =", jsonlite::toJSON(c(LETTERS, sapply(LETTERS, function(x) paste0(x, LETTERS)))), ";", script) Which gives us: cat(script, "\n") 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","AA","AB","AC","AD","AE","AF","AG","AH","AI","AJ","AK","AL","AM","AN","AO","AP","AQ","AR","AS","AT","AU","AV","AW","AX","AY","AZ","BA","BB","BC","BD","BE","BF","BG","BH","BI","BJ","BK","BL","BM","BN","BO","BP","BQ","BR","BS","BT","BU","BV","BW","BX","BY","BZ","CA","CB","CC","CD","CE","CF","CG","CH","CI","CJ","CK","CL","CM","CN","CO","CP","CQ","CR","CS","CT","CU","CV","CW","CX","CY","CZ","DA","DB","DC","DD","DE","DF","DG","DH","DI","DJ","DK","DL","DM","DN","DO","DP","DQ","DR","DS","DT","DU","DV","DW","DX","DY","DZ","EA","EB","EC","ED","EE","EF","EG","EH","EI","EJ","EK","EL","EM","EN","EO","EP","EQ","ER","ES","ET","EU","EV","EW","EX","EY","EZ","FA","FB","FC","FD","FE","FF","FG","FH","FI","FJ","FK","FL","FM","FN","FO","FP","FQ","FR","FS","FT","FU","FV","FW","FX","FY","FZ","GA","GB","GC","GD","GE","GF","GG","GH","GI","GJ","GK","GL","GM","GN","GO","GP","GQ","GR","GS","GT","GU","GV","GW","GX","GY","GZ","HA","HB","HC","HD","HE","HF","HG","HH","HI","HJ","HK","HL","HM","HN","HO","HP","HQ","HR","HS","HT","HU","HV","HW","HX","HY","HZ","IA","IB","IC","ID","IE","IF","IG","IH","II","IJ","IK","IL","IM","IN","IO","IP","IQ","IR","IS","IT","IU","IV","IW","IX","IY","IZ","JA","JB","JC","JD","JE","JF","JG","JH","JI","JJ","JK","JL","JM","JN","JO","JP","JQ","JR","JS","JT","JU","JV","JW","JX","JY","JZ","KA","KB","KC","KD","KE","KF","KG","KH","KI","KJ","KK","KL","KM","KN","KO","KP","KQ","KR","KS","KT","KU","KV","KW","KX","KY","KZ","LA","LB","LC","LD","LE","LF","LG","LH","LI","LJ","LK","LL","LM","LN","LO","LP","LQ","LR","LS","LT","LU","LV","LW","LX","LY","LZ","MA","MB","MC","MD","ME","MF","MG","MH","MI","MJ","MK","ML","MM","MN","MO","MP","MQ","MR","MS","MT","MU","MV","MW","MX","MY","MZ","NA","NB","NC","ND","NE","NF","NG","NH","NI","NJ","NK","NL","NM","NN","NO","NP","NQ","NR","NS","NT","NU","NV","NW","NX","NY","NZ","OA","OB","OC","OD","OE","OF","OG","OH","OI","OJ","OK","OL","OM","ON","OO","OP","OQ","OR","OS","OT","OU","OV","OW","OX","OY","OZ","PA","PB","PC","PD","PE","PF","PG","PH","PI","PJ","PK","PL","PM","PN","PO","PP","PQ","PR","PS","PT","PU","PV","PW","PX","PY","PZ","QA","QB","QC","QD","QE","QF","QG","QH","QI","QJ","QK","QL","QM","QN","QO","QP","QQ","QR","QS","QT","QU","QV","QW","QX","QY","QZ","RA","RB","RC","RD","RE","RF","RG","RH","RI","RJ","RK","RL","RM","RN","RO","RP","RQ","RR","RS","RT","RU","RV","RW","RX","RY","RZ","SA","SB","SC","SD","SE","SF","SG","SH","SI","SJ","SK","SL","SM","SN","SO","SP","SQ","SR","SS","ST","SU","SV","SW","SX","SY","SZ","TA","TB","TC","TD","TE","TF","TG","TH","TI","TJ","TK","TL","TM","TN","TO","TP","TQ","TR","TS","TT","TU","TV","TW","TX","TY","TZ","UA","UB","UC","UD","UE","UF","UG","UH","UI","UJ","UK","UL","UM","UN","UO","UP","UQ","UR","US","UT","UU","UV","UW","UX","UY","UZ","VA","VB","VC","VD","VE","VF","VG","VH","VI","VJ","VK","VL","VM","VN","VO","VP","VQ","VR","VS","VT","VU","VV","VW","VX","VY","VZ","WA","WB","WC","WD","WE","WF","WG","WH","WI","WJ","WK","WL","WM","WN","WO","WP","WQ","WR","WS","WT","WU","WV","WW","WX","WY","WZ","XA","XB","XC","XD","XE","XF","XG","XH","XI","XJ","XK","XL","XM","XN","XO","XP","XQ","XR","XS","XT","XU","XV","XW","XX","XY","XZ","YA","YB","YC","YD","YE","YF","YG","YH","YI","YJ","YK","YL","YM","YN","YO","YP","YQ","YR","YS","YT","YU","YV","YW","YX","YY","YZ","ZA","ZB","ZC","ZD","ZE","ZF","ZG","ZH","ZI","ZJ","ZK","ZL","ZM","ZN","ZO","ZP","ZQ","ZR","ZS","ZT","ZU","ZV","ZW","ZX","ZY","ZZ"] ; var Visited = {}; function getSuffix(orgid){ if (Object.keys(Visited).indexOf(orgid) === -1){Visited[orgid] = 0;} else{Visited[orgid]++;} return LETTERS[Visited[orgid]]; }
How to build conditionaPanel on condition of data table entry that was chosen via double click?
I would like to build an app that is made up of several conditionalPanels. The first panel shows a data table when a reactive value is equal to 0. The other two panels show up when the reacitve value is 1. This happens when one double clicks on the data table. However, I would like to have an additional condition for the last two panels. One of the panels has to show up when a specified boolean column takes FALSE for the double clicked row, the other when it is TRUE. As I am not familiar with javascript, I am failing here unfortunately. The code looks like this: library(shiny) library(data.table) library(DT) set.seed(42) A <- rep(0, 10) for (i in seq_len(10)) { random_length <- sample(1:10, 1) random_letters <- sample(letters, random_length) A[i] <- paste0(random_letters, collapse = "") } B <- sample(c(TRUE, FALSE), 10, replace = TRUE) dt <- data.table(A, B) ui <- fluidPage( conditionalPanel( condition = "output.doubleClickOutput == 0", dataTableOutput("tableId") ), conditionalPanel( condition = "output.doubleClickOutput == 1", actionButton("buttonId1", label = "Back 1") ), conditionalPanel( condition = "output.doubleClickOutput == 1", actionButton("buttonId2", label = "Back 2") ), ) server <- function(input, output, session) { observeEvent(input$doubleClickData, { doubleClickIndicator$doubleClick <- 1 }) observeEvent(input$buttonId1, { doubleClickIndicator$doubleClick <- 0 }) observeEvent(input$buttonId2, { doubleClickIndicator$doubleClick <- 0 }) output$doubleClickOutput <- reactive({ doubleClickIndicator$doubleClick }) outputOptions(output, name = "doubleClickOutput", suspendWhenHidden = FALSE) doubleClickIndicator <- reactiveValues(doubleClick = 0) output$tableId <- renderDataTable( dt, options = list(columnDefs = list(list( targets = 2, render = JS( "function(data, type, row, meta) {", " if(type === 'display'){", " return data ? '<input type=\"checkbox\" disabled checked/>' : '<input type=\"checkbox\" disabled/>';", " }", " return data;", "}" ) ))), callback = JS( "table.on('dblclick', 'td', ", "function() {", "var row = table.cell(this).index().row;", "Shiny.setInputValue('doubleClickData', {row});", "}", ");" ) ) } shinyApp(ui, server) Everything works fine except for: conditionalPanel( condition = "output.doubleClickOutput == 1", actionButton("buttonId1", label = "Back 1") ), conditionalPanel( condition = "output.doubleClickOutput == 1", actionButton("buttonId2", label = "Back 2") ), It should be something like this conditionalPanel( condition = "output.doubleClickOutput == 1 && dt[name='B'].rows(input$doubleClickData) == false", actionButton("buttonId1", label = "Back 1") ), conditionalPanel( condition = "output.doubleClickOutput == 1 && dt[name='B'].rows(input$doubleClickData) == true", actionButton("buttonId2", label = "Back 2") ), but with correct javascript. I appreciate any help.
Not sure I understand everything, but what about this: library(shiny) library(data.table) # why do you use data.table? library(DT) set.seed(42) A <- rep(0, 10) for (i in seq_len(10)) { random_length <- sample(1:10, 1) random_letters <- sample(letters, random_length) A[i] <- paste0(random_letters, collapse = "") } B <- sample(c(TRUE, FALSE), 10, replace = TRUE) dt <- data.table(A, B) ui <- fluidPage( conditionalPanel( condition = "output.doubleClickOutput == 0", dataTableOutput("tableId") ), conditionalPanel( condition = "output.doubleClickOutput == 1 && input.doubleClickData == false", actionButton("buttonId1", label = "Back 1") ), conditionalPanel( condition = "output.doubleClickOutput == 1 && input.doubleClickData == true", actionButton("buttonId2", label = "Back 2") ), ) server <- function(input, output, session) { doubleClickIndicator <- reactiveValues(doubleClick = 0) observeEvent(input[["doubleClickData"]], { doubleClickIndicator[["doubleClick"]] <- 1 }) observeEvent(input[["buttonId1"]], { doubleClickIndicator[["doubleClick"]] <- 0 }) observeEvent(input[["buttonId2"]], { doubleClickIndicator[["doubleClick"]] <- 0 }) output[["doubleClickOutput"]] <- reactive({ doubleClickIndicator[["doubleClick"]] }) outputOptions( output, name = "doubleClickOutput", suspendWhenHidden = FALSE ) output[["tableId"]] <- renderDataTable( dt, options = list( columnDefs = list( list( targets = 2, render = JS( "function(data, type, row, meta) {", " if(type === 'display'){", " return data ? '<input type=\"checkbox\" disabled checked/>' : '<input type=\"checkbox\" disabled/>';", " }", " return data;", "}" ) ) ) ), callback = JS( "table.on('dblclick', 'td:nth-child(3)', function() {", " var cell = table.cell(this);", " Shiny.setInputValue('doubleClickData', cell.data(), {priority: 'event'});", "});" ) ) observe({ # (test) print(input[["doubleClickData"]]) }) } shinyApp(ui, server) Note that you have to click a little next to the checkboxes, not on the checkboxes. EDIT To get the desired event by clicking anywhere on a row, replace the callback with this one: callback = JS( "table.on('dblclick', 'tr', function() {", " var rowIndex = table.row(this).index();", " var bool = table.cell(rowIndex, 2).data();", " Shiny.setInputValue('doubleClickData', bool, {priority: 'event'});", "});" )
Javascript Datatable Time Sum
I'm trying to do something with JavaScript DataTable but I'm stuck somewhere . There are 2 DataTable on the same page . I am sending data from the first DataTable second DataTable . I can write in the footer section that collects in the second DataTable int . I collect information on hours in a second column in the DataTable I want to write a footer . I could not do it. How do you think I should proceed . Existing employees script below. Thank you to everyone. <script type="text/javascript" language="javascript"> $(document).ready(function() { var t = $('#FlowList').DataTable({ rowReorder: true, "footerCallback": function ( row, data, start, end, display ) { var api = this.api(), data; // Remove the formatting to get integer data for summation var intVal = function ( i ) { return typeof i === 'string' ? i.replace(/[\$,]/g, '')*1 : typeof i === 'number' ? i : 0; }; // Tüm sayfaların toplamı total = api .column( 3 ) .data() .reduce( function (a, b) { return intVal(a) + intVal(b); }, 0 ); // Gösterilen sayfanın toplamı pageTotal = api .column( 3, { page: 'current'} ) .data() .reduce( function (a, b) { return intVal(a) + intVal(b); }, 0 ); // Footer Güncelleme $( api.column( 3 ).footer() ).html( /* '$'+pageTotal +' ( $'+ total +' Toplam)' */ 'Toplam ' + total ); } }); var counter = 1; var table = $('#NewsListTable').DataTable(); $('#NewsListTable tbody').on('dblclick', 'tr', function(){ var data = table.row( this ).data(); //alert(data[3]); t.row.add( [ counter +'', //Sıra numarasını her seferinde 1 arttırıyoruz. data[1], data[2], data[3], data[4], data[5], data[6], data[7], data[8] ] ).draw( false ); counter++; }); }); </script>
DataTables combine FooterCallback and select filtering
Good evening. I had some problems combining two DataTables functions (see title). This (FooterCallback) to display the sum of a column with numeric values: $(document).ready(function() { $('#pr_table').DataTable( { "footerCallback": function ( row, data, start, end, display ) { var api = this.api(), data; // Remove the formatting to get integer data for summation var intVal = function ( i ) { return typeof i === 'string' ? i.replace(/[\$,]/g, '')*1 : typeof i === 'number' ? i : 0; }; // Total over all pages total = api .column( 4 ) .data() .reduce( function (a, b) { return intVal(a) + intVal(b); }, 0 ); // Total over this page pageTotal = api .column( 4, { page: 'current'} ) .data() .reduce( function (a, b) { return intVal(a) + intVal(b); }, 0 ); // Update footer $( api.column( 4 ).footer() ).html( 'cub.m'+pageTotal +' ( cub.m'+ total +' total)' ); } } ); } ); With this (select filtering): $(document).ready(function() { $('#pr_table').DataTable( { "initComplete": function () { var api = this.api(); api.columns().indexes().flatten().each( function ( i ) { var column = api.column( i ); var select = $('<select><option value=""></option></select>') .appendTo( $(column.header()).empty() ) .on( 'change', function () { var val = $.fn.dataTable.util.escapeRegex( $(this).val() ); column .search( val ? '^'+val+'$' : '', true, false ) .draw(); } ); column.data().unique().sort().each( function ( d, j ) { select.append( '<option value="'+d+'">'+d+'</option>' ) } ); } ); } } ); } ); If I use only one of the functions in my script, everything is working just fine. But trying to combine them results in DataTables failing to initialize at all (just raw html table shows up). I am relatively unfamiliar with javascript this advanced, so it would help me if someone would give me an example of the script with a little explanation how to use multiple functions in one initialization.
R datatable rowCallback with DT
I am trying to perform two distinct formatting operations on a datatable object using the DT and magrittr packages. One uses the helper function formatRound() and the other is passed in as JavaScript to a rowCallback option in the datatable function. When I run either of the formatting operations individually the datatable renders with the expected formatting. However, when I run both together the datatable renders blank but I do not get an error. This code shows the behavior I am describing. library(magrittr) library(DT) df = data.frame(matrix(rnorm(20), nrow=10)) datatable( data = df ) %>% formatRound(c("X1", "X2"), 1) #table renders as expected datatable( data = df, options = list( rowCallback = JS(" function( row, data, index ) { if ( index > 2 ) { $(row).css('background-color', '#EDEDED'); } else if ( index > 0 ) { $(row).css('background-color', '#DEDEDE'); } else { $(row).css('background-color', '#D3D3D3'); } }" ) ) ) #table renders as expected datatable( data = df, options = list( rowCallback = JS(" function( row, data, index ) { if ( index > 2 ) { $(row).css('background-color', '#EDEDED'); } else if ( index > 0 ) { $(row).css('background-color', '#DEDEDE'); } else { $(row).css('background-color', '#D3D3D3'); } }" ) ) ) %>% formatRound(c("X1", "X2"), 1) #table renders as blank but with no error returned
If you have a look at the JS function of your third attempt in the browser's JS console(click on "Inspect Element option in browser"), it shows up an error saying that 'var' is unidentified because it is outside the scope of the JS function: ( var d = parseFloat(data[1]); $(this.api().cell(row, 1).node()).html(isNaN(d) ? '' : d.toFixed(1)); var d = parseFloat(data[2]); $(this.api().cell(row, 2).node()).html(isNaN(d) ? '' : d.toFixed(1)); function( row, data, index ) { if ( index > 2 ) { $(row).css('background-color', '#EDEDED'); } else if ( index > 0 ) { $(row).css('background-color', '#DEDEDE'); } else { $(row).css('background-color', '#D3D3D3'); } }) If you put those two lines inside the JS function, it works perfectly. You can find the updated code below. datatable( data = df, options = list( rowCallback = JS(" function( row, data, index ) { var d = parseFloat(data[1]); $(this.api().cell(row, 1).node()).html(isNaN(d) ? '' : d.toFixed(1)); var d = parseFloat(data[2]); $(this.api().cell(row, 2).node()).html(isNaN(d) ? '' : d.toFixed(1)); if ( index > 2 ) { $(row).css('background-color', '#EDEDED'); } else if ( index > 0 ) { $(row).css('background-color', '#DEDEDE'); } else { $(row).css('background-color', '#D3D3D3'); } }" ) ) )