Carl
Carl

Reputation: 5779

Generating dynamic number of datatables without rerendering

I am wondering what the best practice is for handling a dynamic number of datatables. Here is a toy example:

library(shiny)
library(DT)
ui <- shinyUI(fluidPage(
  mainPanel(
    sliderInput("number","Number of tables",1,10,1,1),
    uiOutput("tables")
  )))

server <- shinyServer(function(input, output, session) {

  observe({
    lapply(seq_len(input$number), function(i) {
      output[[paste0("table",i)]] <- DT::renderDataTable(head(mtcars))
    })
  })

  output$tables <- renderUI({
    lapply(seq_len(input$number), function(i) {
      DT::dataTableOutput(paste0("table",i))
    })
  })


})
# Run the application
shinyApp(ui = ui, server = server)

This approach is sort of a blunt tool, because you have to rerender all the datatables, whenever a single datatable is added or removed.

Is there a better approach to dynamically generating output that doesn't require creating all the output in a loop, and therefore recreating all the output each time there is a change?

Upvotes: 0

Views: 232

Answers (2)

Xiongbing Jin
Xiongbing Jin

Reputation: 12087

[Edit] Answer has been updated with the workaround from @Bárbara Borges (see her answer for details on why it works)

Here is an example, but note that it is working for normal tables (no refresh), but for datatables, there is no refresh when removing tables but always refreshing when adding tables. I think this is something caused by DT but haven't found the real cause yet. I am posting in the hope that someone can improve this.

library(shiny)
library(DT)

numUI <- 0

ui <- shinyUI(fluidPage(
  mainPanel(
    sliderInput("number","Number of tables",1,10,1,1),
    tags$div(id="tables")
  )))

server <- shinyServer(function(input, output, session) {

  observe({
    if (input$number > numUI) {
      for (num in (numUI+1):input$number) {
        insertUI("#tables", "beforeBegin", DT::dataTableOutput(paste0("table", num)))
        output[[paste0("table",num)]] <- DT::renderDataTable(head(mtcars), server = FALSE)
      }
    }

    if (input$number < numUI) {
      for (num in (input$number+1):numUI) {
        removeUI(paste0("#table", num))
      }
    }

    numUI <<- input$number
  })

})
# Run the application
shinyApp(ui = ui, server = server)

Upvotes: 1

B&#225;rbara Borges
B&#225;rbara Borges

Reputation: 919

I'm the author of insertUI and removeUI. It seems like you found a bug in insertUI when applied to interactive outputs. I filed an issue for this in the Shiny repo and will try to get to it soon. In the meantime, a workaround is to use where = "beforeBegin" instead of where = "beforeEnd" in the call to insertUI(). See my answer to the original issue filed in the DT repo for more details.

Upvotes: 1

Related Questions