问题描述
我正在开发一个使用 DT
包和一些 JavaScript
创建漂亮嵌套表的 Shiny 应用程序。在子表中,用户可以编辑第二列(混合 (%))。编辑该列后,第 6 列(GRP/TRP)也将更新。更新混合 (%) 时,我试图存储总 GRP/TRP(或点),然后使用该总数来更新 GRP/TRP 列。请参阅下面的等式。但是,编辑混合百分比时,总分不会正确更新。
主要问题在CosterTableJS
中的footerCallback
totalGRPs/TRPs = sum(GRPs/TRPs 列)
GRPs/TRPs = (mix%/100)*totalGRPs/TRPs
以下也将使用 https://github.com/ejbeaty/CellEdit(MIT 许可)
# parent table
parent_table <- data.frame(structure(list(Market = "ABILENE-SWEETWATER",`Total GRPs/TRPs` = 500L,Cost = 82500,Impressions = 877215,Population = 175443),.Names = c("Market","Total GRPs/TRPs","Cost","Impressions","Population"),row.names = c(NA,-1L),class = "data.frame"))
# child table
child_table <- structure(list(Market = c("ABILENE-SWEETWATER","ABILENE-SWEETWATER","ABILENE-SWEETWATER"),Daypart = c("Early Morning","Daytime","Early Fringe","Early News","Prime Access","Prime Time","Late News","Late Fringe","Non-Linear"),`Mix (%)` = c(10,10,20),Week = c(1,1,1),Weeks = c(5L,5L,5L),`Override CPP` = c(16,18,17,24,27,55,43,22,58),`GRPs/TRPs` = c(10,Cost = c(160,180,170,240,270,550,430,220,1160),Impressions = c(17544.3,17544.3,35088.6),Population = c(175443,175443,175443
),`Full Flight Cost` = c(800,900,850,1200,1350,2750,2150,1100,5800),`Full Flight GRPs/TRPs` = c(50,50,100),`Full Flight Impressions` = c(87721.5,87721.5,175443)),"Daypart","Mix (%)","Week","Weeks","Override CPP","GRPs/TRPs","Population","Full Flight Cost","Full Flight GRPs/TRPs","Full Flight Impressions"),-9L),class = "data.frame")
# 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)) "<img src=\'https://raw.githubusercontent.com/DataTables/DataTables/master/examples/resources/details_open.png\'/>" else "")
cbind(" " = oplus,dat,"_details" = I(subdats),stringsAsFactors = FALSE)
}
table <- nestedData(
dat = parent_table,children = split(child_table,child_table$Market)
)
# Whether to show row names (set TRUE or FALSE)
rowNames <- FALSE
colIdx <- as.integer(rowNames)
parentRows <- which(table[,1] != "")
# Some JS for the table
coster_callback_js <- CosterTableJS(
parentRows,colIdx
)
# Table
dtable <- datatable(
table,callback = coster_callback_js,rownames = rowNames,escape = -colIdx-1,style = "bootstrap4",extensions = 'Buttons',options = list(
dom = "Bt",columnDefs = list(
list(width = '30px',targets = 0),list(width = '300px',targets = 1),list(width = '80px',targets = 3),list(visible = FALSE,targets = c(2,3,4,5)),targets = ncol(table)-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)
path <- "/Users/timmcwilliams/Desktop" # folder containing the file dataTables.cellEdit.js
dep <- htmltools::htmlDependency(
"CellEdit","1.0.19",path,script = "dataTables.cellEdit.js")
dtable$dependencies <- c(dtable$dependencies,list(dep))
dtable
CosterTableJS <- function(parentRows,colIdx) {
return(
JS(
"function onUpdate(updatedCell,updatedRow,oldValue) {","}","function renderInt(data,type,row) {"," if(type === 'display') {"," return parseFloat(data).toFixed(0);"," } else {"," return data;"," }","function renderDollars(data," return '$' + parseFloat(data).toFixed(2);",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{",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>'"," html += '<th></th>';"," return html + '</tr></tfoot></table>';"," return '';","};","// row callback to style the rows of the child tables","var rowCallback = function(row,displayNum,index){"," if($(row).hasClass('odd')){"," $(row).css('background-color','white');"," $(row).hover(function(){"," $(this).css('background-color','lightgreen');"," },function() {"," });",'lightblue');","// 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"," var footerCallback = function(tfoot," $('th',tfoot).css('background-color','#F5F2F2');"," var api = this.api();"," var cpp = api.column(5).data();"," var mix = api.column(2).data();"," var points = api.column(6).data();"," var population = api.column(9).data();"," // HERE"," // Grab the sum of the points to use later on"," var pointsSumTotal = 0;"," for(var i = 0; i < points.length; i++) {"," pointsSumTotal += (parseFloat(points[i]));"," console.log('points',pointsSumTotal)"," // Update the Point column when the Mix column is edited"," for (var z = 0; z < mix.length; z++) {"," api.cell(z,6).data((mix[z]/100)*pointsSumTotal);"," // Update the Cost and Impressions with the new Points values"," for (var i = 0; i < points.length; i++) {"," // The cost"," api.cell(i,7).data(parseFloat(points[i]).toFixed(0)*parseFloat(cpp[i]));"," // The Imps",8).data((((parseFloat(points[i]).toFixed(0)/100)*parseFloat(population[i]))));"," // Update the full flight cost,points,and imps"," var weeks = api.column(4).data();"," var cost = api.column(7).data();"," var imps = api.column(8).data();"," for (var i = 0; i < weeks.length; i++) {"," // The full flight cost",10).data(parseFloat(weeks[i])*parseFloat(cost[i]));"," // The full flight points",11).data(parseFloat(weeks[i])*parseFloat(points[i]).toFixed(0));"," // The full flight imps",12).data(parseFloat(weeks[i])*parseFloat(imps[i]).toFixed(0));","// Make the footer sums"," api.columns().eq(0).each(function(index){"," if(index == 1) return $(api.column(index).footer()).html('Totals');"," 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) {"," $(api.column(index).footer()).html('');"," } else if (index == 7 || index == 10) {"," $(api.column(index).footer()).html('$' + total);"," } else {"," $(api.column(index).footer()).html(total);"," }"," if(index == 2 && total == 100) {"," $(api.column(index).footer()).css({'color': 'green'});"," } else if (index == 2 && total > 100) {"," $(api.column(index).footer()).css({'color': 'red'});"," $(api.column(index).footer()).css({'color': 'black'});"," Shiny.setInputValue('costerData:costernestedData',table.data().toArray());"," })"," // TO HERE"," var n = d.length - 1;"," var id = 'table#' + childId;"," var columns = Object.keys(d[n][0]).map(function(x){"," return {data: x,title: x};"," var subtable = $(id).DataTable({"," 'data': d[n]," 'columns': columns," 'autoWidth': false," 'deferRender': true," 'info': false," 'lengthChange': false," 'ordering': d[n].length > 1," 'order': []," 'paging': false," 'scrollX': false," 'scrollY': false," 'searching': false," 'sortClasses': false," 'pageLength': 50," 'rowCallback': rowCallback," 'headerCallback': headerCallback," 'footerCallback': footerCallback," 'columnDefs': ["," {targets: [4,2,6,8,11,12],render: renderInt}," {targets: [5,7,10],render: renderDollars}," {targets: [0,9],visible: false}," {targets: [1],width: '75px'}," {targets: [2]," {targets: [3],width: '135px'}," {targets: [4]," {targets: [5],width: '100px'}," {targets: [6],width: '80px'}," {targets: '_all',className: 'dt-center'}"," ]"," });"," subtable.MakeCellsEditable({"," onUpdate: onUpdate," inputCss: 'my-input-class'," columns: [2]," inputTypes: ["," {column: 2,type: 'number'}," ]," confirmationButton: {"," confirmCss: 'my-confirm-class'," cancelCss: 'my-cancel-class'","// display the child table on click","// array to store the id's of the already created child tables","var children = [];","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\"/>');"," var childId = tblId + '-child-' + rowIdx;","// this child table has not been created yet"," if(children.indexOf(childId) === -1){"," 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(),rowIdx);"," }else{"," row.child(true);","});"
)
)
}
需要将此作为 dataTables.cellEdit.css
.my-input-class {
padding: 3px 6px;
border: 1px solid #ccc;
border-radius: 4px;
}
.my-confirm-class {
padding: 3px 6px;
font-size: 12px;
color: white;
text-align: center;
vertical-align: middle;
border-radius: 4px;
background-color: #337ab7;
text-decoration: none;
}
.my-cancel-class {
padding: 3px 6px;
font-size: 12px;
color: white;
text-align: center;
vertical-align: middle;
border-radius: 4px;
background-color: #a94442;
text-decoration: none;
}
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)