I have the dt table with nested dropdown lists. Lists work fine until the table is being re-rendered (for example, i need to add extra record to DT table).
Lists work but their values are not updated (print(input$<list name> generates values prior to re-rendering of the table).
i have used the example in the answer section of here.
I expected to have dropdown lists with values updated in input$, like the list "sel1" is updating vlue in input$sel1. Upon the values updated output$sel should be updated as well.
The option i have found is to re-render the table with dropdown lists having different ids each time the table is being rendered. but it looks not efficient and clumsy.
I have extended the script from the example with action button to re-render the table. The app is as follows:
library(shiny)
library(DT)
# this selectInput will not be included in the app;
# we just use it to extract the required HTML dependencies
select_input <- selectInput("x", label = NULL, choices = c("A", "B"))
deps <- htmltools::findDependencies(select_input)
# now you just have to include tagList(deps) somewhere in the Shiny UI
ui <- fluidPage(
actionButton('my_btn', 'Re-render DT table'),
title = 'Selectinput column in a table',
DTOutput('foo'),
verbatimTextOutput('sel'),
tagList(deps)
)
server <- function(input, output, session) {
data <- head(iris, 5)
for (i in 1:nrow(data)) {
data$species_selector[i] <-
as.character(
selectInput(
paste0("sel", i), "",
choices = unique(iris$Species),
width = "100px"
)
)
}
output$foo = renderDT({
datatable(
data, escape = FALSE, selection = 'none',
options = list(
dom = 't',
paging = FALSE,
ordering = FALSE,
initComplete = JS(c(
"function(settings, json) {",
" var $table = this.api().table().node().to$();",
" $table.find('[id^=sel]').selectize();", # apply selectize() to all elements whose id starts with 'sel' (here sel1, sel2, ...)
"}"
)),
preDrawCallback =
JS("function() {Shiny.unbindAll(this.api().table().node());}"),
drawCallback
= JS("function() {Shiny.bindAll(this.api().table().node());}")
)
)
})
output$sel = renderPrint({
str(sapply(1:nrow(data), function(i) input[[paste0("sel", i)]]))
})
observeEvent(input$my_btn,{
output$foo = renderDT({
datatable(
data, escape = FALSE, selection = 'none',
options = list(
dom = 't',
paging = FALSE,
ordering = FALSE,
initComplete = JS(c(
"function(settings, json) {",
" var $table = this.api().table().node().to$();",
" $table.find('[id^=sel]').selectize();", # apply selectize() to all elements whose id starts with 'sel' (here sel1, sel2, ...)
"}"
)),
preDrawCallback =
JS("function() {Shiny.unbindAll(this.api().table().node());}"),
drawCallback
= JS("function() {Shiny.bindAll(this.api().table().node());}")
)
)
})
print(input)
print(input$sel1)
})
}
I never clearly understood why, but you have to unbind when re-rendering the table. You can do that with the callback:
As a side note, don't put an output slot inside an observer (unless there's really no other way). Here you can simply insert
input$my_btninside therenderDT.As another side note, you should avoid to re-render the table. If you have to add new records in your real app, use a proxy and the DT function
replaceDatainstead.Full code: