Reputation: 602
I'm trying to create a datatable with Shiny input elements (checkboxInput or textInput). This works well until I update the datatable. If I add more rows with more input elements, only the new elements work. I thought the table would be recreated every time I update it and the ids would be associated with the new input elements. The code example below illustrates the problem. It creates a table with one row first. If I then create a table with two rows using the dropdown on the left, I can only read the values of the second row in the output table. Any change to the inputs of the first row has no impact on the ouput table.
library(DT)
library(shiny)
server <- function(input, output) {
updateTable <- reactive({
num <- as.integer(input$num)
df <- data.frame(check = unlist(lapply(1:num, function(i) as.character(checkboxInput(paste0("check_", i), label = paste0("check", i), value = 0)))),
text = unlist(lapply(1:num, function(i) as.character(textInput(paste0("text_",i), label = paste0("text", i), value = "")))))
})
output$input_ui <- DT::renderDataTable(
updateTable(),
server = FALSE, escape = FALSE, selection = 'none',
options = list(
dom = 't', paging = FALSE, ordering = FALSE,lengthChange = TRUE,
preDrawCallback = JS('function() { Shiny.unbindAll(this.api().table().node()); }'),
drawCallback = JS('function() { Shiny.bindAll(this.api().table().node()); } ')
)
)
output$table <- renderTable({
num <- as.integer(input$num)
data.frame(lapply(1:num, function(i) {
paste(input[[paste0("check_", i)]], input[[paste0("text_",i)]], sep = " : ")
}))
})
}
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
selectInput("num", "select number of inputs", choices = seq(1,10,1))
),
mainPanel(
DT::dataTableOutput("input_ui"),
tableOutput("table")
)
)
)
shinyApp(ui = ui, server = server)
Upvotes: 2
Views: 1308
Reputation: 5932
A possible solution is provided here:
https://groups.google.com/d/msg/shiny-discuss/ZUMBGGl1sss/7sdRQecLBAAJ
As far as I understand, it allows to "force" a complete unbind of all checkbox/textinpts before redrawing the table thanks to the use of:
session$sendCustomMessage('unbind-DT', 'input_ui')
. I do not pretend to really understsand it, but apparently it works. See below for a possible implementation.
library(shiny)
library(DT)
server <- function(input, output,session) {
updateTable <- reactive({
num <- as.integer(input$num)
session$sendCustomMessage('unbind-DT', 'input_ui')
df <- data.frame(
check = unlist(lapply(1:num, function(i) as.character(checkboxInput(paste0("check_", i), label = paste0("check", i), value = 0)))),
text = unlist(lapply(1:num, function(i) as.character(textInput(paste0("text_",i), label = paste0("text", i), value = "")))))
tbl <- DT::datatable(df, escape = FALSE,
selection = "none",
options = list(
dom = 't', paging = FALSE, ordering = FALSE,lengthChange = TRUE,
preDrawCallback = JS('function() { Shiny.unbindAll(this.api().table().node()); }'),
drawCallback = JS('function() { Shiny.bindAll(this.api().table().node()); } ')
))
})
output$input_ui <- DT::renderDataTable(
updateTable(),
server = FALSE
)
output$table <- renderTable({
num <- as.integer(input$num)
data.frame(lapply(1:num, function(i) {
paste(input[[paste0("check_", i)]], input[[paste0("text_",i)]], sep = " : ")
}))
})
}
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
selectInput("num", "select number of inputs", choices = seq(1,10,1))
),
mainPanel(
DT::dataTableOutput("input_ui"),
tags$script(HTML(
"Shiny.addCustomMessageHandler('unbind-DT', function(id) {
Shiny.unbindAll($('#'+id).find('table').DataTable().table().node());
})")),
tableOutput("table")
)
)
)
shinyApp(ui = ui, server = server)
HTH!
Upvotes: 2