我在闪亮的应用程序中嵌套了DataTable
,该嵌套由以下数据制成。我有两个滑块,它们占100%的百分比。如果一个滑块为50,另一边为50。两个滑块中的这两个数字有助于组成子表的Spot:30(%)
和Spot:15(%)
列。
[还有另一列Mix(%)
,用户可以在其中输入和编辑数字。当用户编辑此列时,Spot:30(%)
和Spot:15(%)
列中的数字应该相应地进行更新。
等式为:
Spot:30(%) = (Mix(%) * slider_value1)/100
Spot:15(%) = (Mix(%) * slider_value2)/100
[当用户编辑JS callback
列时,是否可以使用Spot:30(%)
脚本中Shiny应用程序中的滑块值来更新Spot:15(%)
和Mix(%)
列?
[我已经尝试尝试像example1这样的解决方案,并尝试遵循communicating with shiny via javascript,但是似乎无法解决这个问题。
数据
Parent
structure(list(Market = c("ABILENE-SWEETWATER", "ALBANY-SCHENECTADY-TROY, NY"
), `Gross CPP` = c("$0", "$0"), `Gross CPM` = c("$0", "$0"),
`Historical Composite Gross CPP` = c("$0", "$0"), `Historical Composite Gross CPM` = c("$0",
"$0")), .Names = c("Market", "Gross CPP", "Gross CPM", "Historical Composite Gross CPP",
"Historical Composite Gross CPM"), row.names = c(NA, -2L), class = "data.frame")
Child
structure(list(Market = c("ABILENE-SWEETWATER", "ABILENE-SWEETWATER",
"ABILENE-SWEETWATER", "ABILENE-SWEETWATER", "ABILENE-SWEETWATER",
"ABILENE-SWEETWATER", "ABILENE-SWEETWATER", "ABILENE-SWEETWATER",
"ABILENE-SWEETWATER", "ABILENE-SWEETWATER", "ALBANY-SCHENECTADY-TROY, NY",
"ALBANY-SCHENECTADY-TROY, NY", "ALBANY-SCHENECTADY-TROY, NY",
"ALBANY-SCHENECTADY-TROY, NY", "ALBANY-SCHENECTADY-TROY, NY",
"ALBANY-SCHENECTADY-TROY, NY", "ALBANY-SCHENECTADY-TROY, NY",
"ALBANY-SCHENECTADY-TROY, NY", "ALBANY-SCHENECTADY-TROY, NY",
"ALBANY-SCHENECTADY-TROY, NY"), Daypart = c("Daytime", "Early Fringe",
"Early Morning", "Early News", "Late Fringe", "Late News", "Prime Access",
"Prime Time", "tv_2", "tv_cross_screen", "Daytime", "Early Fringe",
"Early Morning", "Early News", "Late Fringe", "Late News", "Prime Access",
"Prime Time", "tv_2", "tv_cross_screen"), `Mix (%)` = c(15, 10,
15, 10, 5, 5, 10, 10, 0, 0, 15, 10, 15, 10, 5, 5, 10, 10, 0,
0), `Spot:30 (%)` = c(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0), `Spot:15 (%)` = c(0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), `Gross CPP ($)` = c(18,
18, 16, 23, 24, 40, 26, 44, 0, 0, 77, 71, 61, 78, 109, 145, 93,
213, 0, 0), `Gross CPM ($)` = c(0, 0, 0, 0, 0, 0, 0, 0, 23, 13,
0, 0, 0, 0, 0, 0, 0, 0, 23, 13), `Historical Override CPP ($)` = c(18,
18, 16, 23, 24, 40, 26, 44, 0, 0, 77, 71, 61, 78, 109, 145, 93,
213, 0, 0), `Historical Override CPM ($)` = c(0, 0, 0, 0, 0,
0, 0, 0, 23, 13, 0, 0, 0, 0, 0, 0, 0, 0, 23, 13)), .Names = c("Market",
"Daypart", "Mix (%)", "Spot:30 (%)", "Spot:15 (%)", "Gross CPP ($)",
"Gross CPM ($)", "Historical Override CPP ($)", "Historical Override CPM ($)"
), class = "data.frame", row.names = c(NA, -20L))
编辑
出于某些原因,当我移动滑块时,Shiny Server会观察到该事件并打印出新的滑块值,但是新的滑块值不会传递给JS回调,而JS回调又不会更新DataTable。数据表停留在var slider = 50
,并且不更新。
代码
# The datatable callback
parentRows <- which(Dat[,1] != "")
callback_js = JS(
"var tv_spots30 = 50;",
"Shiny.addCustomMessageHandler('sliderValue1', function(value1){",
" tv_spots30 = value1;",
" $('#' + children[0]).DataTable().draw()",
" $('#' + children[1]).DataTable().draw();",
"});",
"var spots15 = 50;",
"Shiny.addCustomMessageHandler('sliderValue2', function(value2){",
" spots15 = value2;",
" $('#' + children[0]).DataTable().draw();",
" $('#' + children[1]).DataTable().draw();",
"});",
"function onUpdate(updatedCell, updatedRow, oldValue) {};",
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({",
" 'border-top': '3px solid green',",
" '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();",
" var col_mix = api.column(2).data();",
" for(let i = 0; i < col_mix.length; i++){",
" api.cell(i,3).data(parseFloat(col_mix[i])*tv_spots30/100);",
" api.cell(i,4).data(parseFloat(col_mix[i])*spots15/100);",
" }",
" 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 parseFloat(a) + parseFloat(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 += (parseFloat(col_share[i])*parseFloat(col_CPP[i]).toFixed(2));",
" CPM += (parseFloat(col_share[i])*parseFloat(col_CPM[i]).toFixed(2));",
" Historical_CPP += (parseFloat(col_share[i])*parseFloat(col_Historical_CPP[i]).toFixed(2));",
" Historical_CPM += (parseFloat(col_share[i])*parseFloat(col_Historical_CPM[i]).toFixed(2));",
" }",
" 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: '_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: -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('⊕');",
" } 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('⊖');",
" format_datatable(row.data(), childId, rowIdx);",
" }else{",
" row.child(true);",
" td.html('⊖');",
" }",
" }",
"});")
# Module to create the nested structure of the table
NestedData <- function(dat, children){
stopifnot(length(children) == nrow(dat))
g <- function(d){
if(is.data.frame(d)){
purrr::transpose(d)
}else{
purrr::transpose(NestedData(d[[1]], children = d$children))
}
}
subdats <- lapply(children, g)
oplus <- sapply(subdats, function(x) if(length(x)) "⊕" else "")
cbind(" " = oplus, dat, "_details" = I(subdats), stringsAsFactors = FALSE)
}
# shiny
# Bind the market level and mix breakout data together for the final table
market_mix_table <- reactive({
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()
# Table
table <- DT::datatable(
Dat,
callback = callback_js,
rownames = rowNames,
escape = -colIdx-1,
style = "bootstrap4",
options = list(
lengthMenu = list(
c(-1, 10, 20),
c("All", 10, 20)
),
columnDefs = list(
list(width = '30px'),
list(width = '100px', targets = 1),
list(visible = FALSE, targets = ncol(Dat)-1+colIdx),
list(orderable = FALSE, className = 'details-control', targets = colIdx),
list(className = "dt-center", targets = "_all")
)
)
)
# Some faancy Java magic
path <- getwd()
# Call the html tools deps (js & css files in this directory)
dep <- htmltools::htmlDependency(
"CellEdit", "1.0.19", path,
script = "dataTables.cellEdit.js", stylesheet = "dataTables.cellEdit.css")
table$dependencies <- c(table$dependencies, list(dep))
# server = FALSE
return(table)
})
observeEvent(input[["tv_spots30"]], {
print("30%")
print(input[["tv_spots30"]])
session$sendCustomMessage("sliderValue1", input[["tv_spots30"]])
})
observeEvent(input[["spots15"]], {
print("15%")
print(input[["spots15"]])
session$sendCustomMessage("sliderValue2", input[["spots15"]])
})
# ui
sliderInput("tv_spots30",
"Spots 30 (%):",
min = 0,
max = 100,
value = 50,
step = 5
),
sliderInput("spots15",
"Spots 15 (%):",
min = 0,
max = 100,
value = 50,
step = 5
),
这是我取得的成就。
在回调的开头:
"var slider = 50;",
"Shiny.addCustomMessageHandler('sliderValue', function(value){",
" slider = value;",
" $('#' + children[0]).DataTable().draw();",
" $('#' + children[1]).DataTable().draw();",
"});",
在服务器中:
observeEvent(input[["slider"]], {
session$sendCustomMessage("sliderValue", input[["slider"]])
})
并且在页脚回调中:
" var col_mix = api.column(2).data();",
" for(let i = 0; i < col_mix.length; i++){",
" api.cell(i,3).data(parseFloat(col_mix[i])*slider/100);",
" api.cell(i,4).data(parseFloat(col_mix[i])*(100-slider)/100);",
" }",
完整代码:
library(DT)
df_children <- structure(
list(
Market = c("ABILENE-SWEETWATER", "ABILENE-SWEETWATER", "ABILENE-SWEETWATER", "ABILENE-SWEETWATER", "ABILENE-SWEETWATER", "ABILENE-SWEETWATER", "ABILENE-SWEETWATER", "ABILENE-SWEETWATER", "ABILENE-SWEETWATER", "ABILENE-SWEETWATER", "ALBANY-SCHENECTADY-TROY, NY", "ALBANY-SCHENECTADY-TROY, NY", "ALBANY-SCHENECTADY-TROY, NY", "ALBANY-SCHENECTADY-TROY, NY", "ALBANY-SCHENECTADY-TROY, NY", "ALBANY-SCHENECTADY-TROY, NY", "ALBANY-SCHENECTADY-TROY, NY", "ALBANY-SCHENECTADY-TROY, NY", "ALBANY-SCHENECTADY-TROY, NY", "ALBANY-SCHENECTADY-TROY, NY"),
Daypart = c("Daytime", "Early Fringe", "Early Morning", "Early News", "Late Fringe", "Late News", "Prime Access", "Prime Time", "tv_2", "tv_cross_screen", "Daytime", "Early Fringe", "Early Morning", "Early News", "Late Fringe", "Late News", "Prime Access", "Prime Time", "tv_2", "tv_cross_screen"),
`Mix (%)` = c(15, 10, 15, 10, 5, 5, 10, 10, 0, 0, 15, 10, 15, 10, 5, 5, 10, 10, 0, 0),
`Spot:30 (%)` = c(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
`Spot:15 (%)` = c(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
`Gross CPP ($)` = c(18, 18, 16, 23, 24, 40, 26, 44, 0, 0, 77, 71, 61, 78, 109, 145, 93, 213, 0, 0),
`Gross CPM ($)` = c(0, 0, 0, 0, 0, 0, 0, 0, 23, 13, 0, 0, 0, 0, 0, 0, 0, 0, 23, 13),
`Historical Override CPP ($)` = c(18, 18, 16, 23, 24, 40, 26, 44, 0, 0, 77, 71, 61, 78, 109, 145, 93, 213, 0, 0),
`Historical Override CPM ($)` = c(0, 0, 0, 0, 0, 0, 0, 0, 23, 13, 0, 0, 0, 0, 0, 0, 0, 0, 23, 13)
),
.Names = c("Market", "Daypart", "Mix (%)", "Spot:30 (%)", "Spot:15 (%)", "Gross CPP ($)", "Gross CPM ($)", "Historical Override CPP ($)", "Historical Override CPM ($)"),
class = "data.frame",
row.names = c(NA, -20L)
)
df_parent <- structure(
list(
Market = c("ABILENE-SWEETWATER", "ALBANY-SCHENECTADY-TROY, NY"),
`Gross CPP` = c("$0", "$0"),
`Gross CPM` = c("$0", "$0"),
`Historical Composite Gross CPP` = c("$0", "$0"),
`Historical Composite Gross CPM` = c("$0", "$0")
),
.Names = c("Market", "Gross CPP", "Gross CPM", "Historical Composite Gross CPP", "Historical Composite Gross CPM"),
row.names = c(NA, -2L),
class = "data.frame")
# function to make the required dataframe
NestedData <- function(dat, children){
stopifnot(length(children) == nrow(dat))
g <- function(d){
if(is.data.frame(d)){
purrr::transpose(d)
}else{
purrr::transpose(NestedData(d[[1]], children = d$children))
}
}
subdats <- lapply(children, g)
oplus <- sapply(subdats, function(x) if(length(x)) "⊕" else "")
cbind(" " = oplus, dat, "_details" = I(subdats), stringsAsFactors = FALSE)
}
# make the required dataframe
# one must have: length(children) == nrow(dat)
Dat <- NestedData(
dat = df_parent,
children = split(df_children, df_children$Market)
)
## whether to show row names (set TRUE or FALSE)
rowNames <- FALSE
colIdx <- as.integer(rowNames)
## make the callback
parentRows <- which(Dat[,1] != "")
callback = JS(
"var slider = 50;",
"Shiny.addCustomMessageHandler('sliderValue', function(value){",
" slider = value;",
" $('#' + children[0]).DataTable().draw();",
" $('#' + children[1]).DataTable().draw();",
"});",
"function onUpdate(updatedCell, updatedRow, oldValue) {};",
"table.MakeCellsEditable({",
" onUpdate: onUpdate,",
" inputCss: 'my-input-class',",
" confirmationButton: {",
" confirmCss: 'my-confirm-class',",
" cancelCss: 'my-cancel-class'",
" }",
"});",
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', 'papayawhip');",
" $(row).hover(function(){",
" $(this).css('background-color', '#E6FF99');",
" }, function() {",
" $(this).css('background-color', 'papayawhip');",
" });",
" } else {",
" $(row).css('background-color', 'lemonchiffon');",
" $(row).hover(function(){",
" $(this).css('background-color', '#DDFF75');",
" }, function() {",
" $(this).css('background-color', 'lemonchiffon');",
" });",
" }",
"};",
"",
"// header callback to style the header of the child tables",
"var headerCallback = function(thead, data, start, end, display){",
" $('th', thead).css({",
" 'border-top': '3px solid indigo',",
" 'color': 'indigo',",
" 'background-color': '#fadadd'",
" });",
"};",
"",
"// 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();",
" var col_mix = api.column(2).data();",
" for(let i = 0; i < col_mix.length; i++){",
" api.cell(i,3).data(parseFloat(col_mix[i])*slider/100);",
" api.cell(i,4).data(parseFloat(col_mix[i])*(100-slider)/100);",
" }",
" 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 parseFloat(a) + parseFloat(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 += (parseFloat(col_share[i])*parseFloat(col_CPP[i]).toFixed(2));",
" CPM += (parseFloat(col_share[i])*parseFloat(col_CPM[i]).toFixed(2));",
" Historical_CPP += (parseFloat(col_share[i])*parseFloat(col_Historical_CPP[i]).toFixed(2));",
" Historical_CPM += (parseFloat(col_share[i])*parseFloat(col_Historical_CPM[i]).toFixed(2));",
" }",
" 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': false,",
" 'scrollX': false,",
" 'scrollY': false,",
" 'searching': false,",
" 'sortClasses': false,",
" 'rowCallback': rowCallback,",
" 'headerCallback': headerCallback,",
" 'footerCallback': footerCallback,",
" 'columnDefs': [{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': false,",
" 'scrollX': false,",
" 'scrollY': false,",
" 'searching': false,",
" 'sortClasses': false,",
" 'rowCallback': rowCallback,",
" 'headerCallback': headerCallback,",
" 'footerCallback': footerCallback,",
" 'columnDefs': [",
" {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',",
" 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('⊕');",
" } 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('⊖');",
" format_datatable(row.data(), childId, rowIdx);",
" }else{",
" row.child(true);",
" td.html('⊖');",
" }",
" }",
"});")
## the datatable
dtable <- datatable(
Dat, callback = callback, rownames = rowNames, escape = -colIdx-1,
options = list(
columnDefs = list(
list(visible = FALSE, targets = ncol(Dat)-1+colIdx),
list(orderable = FALSE, className = 'details-control', targets = colIdx),
list(className = "dt-center", targets = "_all")
)
)
)
path <- "~/Work/R/DT" # folder containing the files dataTables.cellEdit.js
# and dataTables.cellEdit.css
dep <- htmltools::htmlDependency(
"CellEdit", "1.0.19", path,
script = "dataTables.cellEdit.js", stylesheet = "dataTables.cellEdit.css")
dtable$dependencies <- c(dtable$dependencies, list(dep))
# Shiny ####
library(shiny)
ui <- fluidPage(
fluidRow(
column(
width = 4,
sliderInput("slider", "Percentage", min = 0, max = 100, value = 50, step = 1)
),
column(
width = 8,
DTOutput("tbl")
)
)
)
server <- function(input, output, session){
output[["tbl"]] <- renderDT(dtable)
observeEvent(input[["slider"]], {
session$sendCustomMessage("sliderValue", input[["slider"]])
})
}
shinyApp(ui, server)