【发布时间】:2020-06-27 21:09:10
【问题描述】:
我的 Shiny 应用中有一个嵌套的 DataTable。子表允许用户进入并手动编辑几列中的值。这里的目标是在表的末尾有一个 totals 行来汇总列。如果用户进入并更改了一个值,那么totals 行将使用新的列总和进行更新。例如,如果我是用户并且想将Daytime 的Share (%) 更改为20,则Totals 行将更新为105,
我能够在常规 DataTable 上创建此功能,但我很难为嵌套子表创建此功能。
子表
structure(list(Daypart = c("Daytime", "Early Fringe", "Early Morning",
"Early News", "Late Fringe", "Late News", "Prime Access", "Prime Time"
), `Share (%)` = c(15, 15, 15, 15, 10, 10, 10, 10), `Spot:30 (%)` = c(0,
0, 0, 0, 0, 0, 0, 0), `Spot:15 (%)` = c(0, 0, 0, 0, 0, 0, 0,
0), `Gross CPM` = c("$0", "$0", "$0", "$0", "$0", "$0", "$0",
"$0")), .Names = c("Daypart", "Share (%)", "Spot:30 (%)", "Spot:15 (%)",
"Gross CPM"), row.names = c(NA, -8L), class = "data.frame")
父表
structure(list(Market = c("ABILENE-SWEETWATER", "ALBANY-SCHENECTADY-TROY, NY"
), `Gross CPP` = c("$0", "$0"), `Gross CPM` = c("$0", "$0"),
`Historical Composite Gross CPP (if applicable)` = c("$0",
"$0"), `Historical Composite Gross CPM (if applicable)` = c("$0",
"$0")), .Names = c("Market", "Gross CPP", "Gross CPM", "Historical Composite Gross CPP (if applicable)",
"Historical Composite Gross CPM (if applicable)"), row.names = c(NA,
-2L), class = "data.frame")
代码
# Module that renders the table
tableMod <- function(input, output, session, runButton, data){
# this variable will be in sync with your datatable
df <- reactiveVal(data)
output$update_table <- DT::renderDataTable({
runButton()
isolate(
datatable(
df() %>%
bind_rows(
summarise_all(.,
funs(
if (is.numeric(.))
sum(.)
else if (is.factor(.)) "-"
else "Sub Total")
)
),
selection = 'none', editable = TRUE
)
)
})
# Observe the event
observeEvent(input$x1_cell_edit, {
new_df <- df()
row <- input$x1_cell_edit$row
col <- input$x1_cell_edit$col
value <- as.numeric(input$x1_cell_edit$value)
new_df[row, col] <- value
df(new_df)
})
list(updated_df = df)
}
# Module used to display the updated table
tableUI <- function(id) {
ns <- NS(id)
dataTableOutput(ns("update_table"))
}
服务器
# Bind the market level and mix breakout data together for the final table
market_mix_table <- reactive({
markets <- market_costings_gross_net()
mix_breakout <- daypart_break_out()
# Need to use replicate() on mix_breakout_table for cases when there is an arbitrary number of rows in markets
n <- nrow(markets)
children_list <- replicate(n, mix_breakout, simplify = FALSE)
# Make the dataframe
# This must be met length(children) == nrow(dat)
Dat <- NestedData(
dat = markets,
children = children_list
)
return(Dat)
})
# Render the table
output$daypartTable <- DT::renderDataTable({
# 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(
callModule(tableMod, "opfun", runButton = reactive(input$opt_run), data = Dat),
callback = callback_js,
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")
)
)
)
# Some faancy Java magic
path <- getwd()
dep <- htmltools::htmlDependency(
"CellEdit", "1.0.19", path,
script = "dataTables.cellEdit.js", stylesheet = "dataTables.cellEdit.css")
table$dependencies <- c(table$dependencies, list(dep))
return(table)
})
用户界面
# Testing out the new observeEvent handling
tableUI("opfun"),
actionButton("opt_run", "Run"),
# Display table
DT::dataTableOutput(
width = "100%",
"daypartTable"
)
【问题讨论】: