Home > Mobile >  Shiny DT datatable input reactivity after table is reloaded
Shiny DT datatable input reactivity after table is reloaded

Time:11-29

My Shiny app contains a DT datatable with a selectInput. The first time the table loads, I can obtain the selectInput value when it changes. However, when the table is reloaded by the user, I am no longer able to obtain the updated selectInput value. One solution would be to rename the selectInput inputId each time the table is reloaded and keep track of the inputId. I came across this post ('Reset inputs' button in shiny app) and hoped that a similar approach would work. Here, I put my DT datatable in a div with an id that is updated each time the table is reloaded. However, it does not work. In my example below, the user reloads the table by clicking "Reload table". After the table is reloaded, input$id1 no longer responds to the dropdown selection in the table. Any suggestions other than renaming the selectinput inputId each time the table is reloaded?

require(shiny)
require(DT)

shinyApp(
  ui = fluidPage(
    actionButton(inputId = "reload", label = "Reload table"),
    uiOutput("resettable_table")
  ),
  server = function(input, output, session) {
    rv <- reactiveValues(times = 1)
    observeEvent(input$reload, {
      rv$times <- rv$times   1
    })
    output$mytable <- DT::renderDataTable({
      input$reload
      DT::datatable(
        data = data.frame(
          Col1 = as.character(selectInput(
            inputId = "id1",
            label = NULL,
            choices = letters
          ))
        ),
        escape = F,
        selection = "none",
        options = list(
          preDrawCallback = JS('function(){Shiny.unbindAll(this.api().table().node());}'),
          drawCallback = JS('function(){Shiny.bindAll(this.api().table().node());}')
        )
      )
    })
    
    output$resettable_table <- renderUI({
      req(rv$times)
      div(
        id = paste0("mydiv", rv$times),
        DT::dataTableOutput("mytable")
      )
    })
    
    observe({
      if(is.null(input$id1)) {
        print("input$id1 is NULL")
      } else {
        print(paste(c("input$id1:", input$id1)))
      }
    })
  }
)

CodePudding user response:

One has to unbind before reloading. But, and I don't know why, one also has to set server = FALSE in renderDT:

shinyApp(
  ui = fluidPage(
    tags$head(tags$script(
      HTML(
        "Shiny.addCustomMessageHandler('unbindDT', function(id) {
            var $table = $('#' id).find('table');
            if($table.length > 0){
              Shiny.unbindAll($table.DataTable().table().node());
            }
          })")
    )),
    actionButton(inputId = "reload", label = "Reload table"),
    uiOutput("resettable_table")
  ),
  server = function(input, output, session) {
    rv <- reactiveValues(times = 1)
    
    observeEvent(input$reload, {
      session$sendCustomMessage("unbindDT", "mytable")
      rv$times <- rv$times   1
    })
    
    output$mytable <- DT::renderDataTable({
      input$reload
      DT::datatable(
        data = data.frame(
          Col1 = as.character(selectInput(
            inputId = "id1",
            label = NULL,
            choices = letters,
            selectize = FALSE
          ))
        ),
        escape = F,
        selection = "none",
        options = list(
          preDrawCallback = JS('function(){Shiny.unbindAll(this.api().table().node());}'),
          drawCallback = JS('function(){Shiny.bindAll(this.api().table().node());}')
        )
      )
    }, server = FALSE)
    
    output$resettable_table <- renderUI({
      req(rv$times)
      div(
        id = paste0("mydiv", rv$times),
        DT::dataTableOutput("mytable")
      )
    })
    
    observe({
      if(is.null(input$id1)) {
        print("input$id1 is NULL")
      } else {
        print(paste(c("input$id1:", input$id1)))
      }
    })
  }
)
  • Related