An economist
An economist

Reputation: 1311

How to create Shiny R dynamic renderTable, with a number of tables determined by the uploaded CSV files?

I am building web app that after uploading csv files transforms the data and then should be able to output few tables. The number of the tables depends strictly on the information included in the csv files, therefore is calculated during the data transformation process. I have created a list lst with data frames that have to be outputted. The length of the list is the number of the tables that should be created. After searching the web I encountered very similar question (here), which is unfortunately not answered yet. Does anyone has an idea how to solve it?

Some of my code (not all, due to significant data transformation), where I would like to replace fixed max_table with variable length(data_set()):

library(shiny)

ui <- fluidPage(
  fluidRow(column(3,
                  wellPanel(
                    fileInput(inputId = "files",
                              label = "Choose cvs files",
                              accept=c('text/csv', 
                                       'text/comma-separated-values,text/plain', 
                                       '.csv'),
                              multiple = TRUE))),
           column(5, offset = 1, 
                  uiOutput("tables")
                  )
           )
  )

max_table <- 5
server <- function(input,output){


  data_set <- reactive({
    if(is.null(input$files)){
      return(NULL)
    }

    lst <- list()
    for(i in 1:length(input$files[,1])){
      lst[[i]] <- read.csv(input$files[[i, 'datapath']], sep = ",", header = TRUE, skip = 4, dec = ".")
    }
    lst
  })

  output$tables <- renderUI({
    plot_output_list <- lapply(1:max_table, function(i) {
      tablename <- paste("tablename", i, sep="")
      tableOutput(tablename)
    })
    do.call(tagList, plot_output_list)
  })


  for (i in 1:max_table){
    local({
      my_i <- i
      tablename <- paste("tablename", my_i, sep="")
      output[[tablename]] <- renderTable({data_set()[[my_i]] 
      })
    })    
  }
}

shinyApp(ui = ui, server = server)

Any help would be much appreciated!

Upvotes: 2

Views: 2568

Answers (1)

Xiongbing Jin
Xiongbing Jin

Reputation: 12107

A solution is to put everything inside an observe.

library(shiny)

ui <- fluidPage(
  fluidRow(column(3,
                  wellPanel(
                    fileInput(
                      inputId = "files",
                      label = "Choose cvs files",
                      accept = c('text/csv',
                                 'text/comma-separated-values,text/plain',
                                 '.csv'),
                      multiple = TRUE
                    )
                  )),
           column(5, offset = 1, uiOutput("tables"))))

server <- function(input, output) {
  observe({
    if (!is.null(input$files)) {
      max_table = length(input$files[, 1])

      lst <- list()
      for (i in 1:length(input$files[, 1])) {
        lst[[i]] <-
          read.csv(
            input$files[[i, 'datapath']],
            sep = ",",
            header = TRUE,
            skip = 4,
            dec = "."
          )
      }

      output$tables <- renderUI({
        plot_output_list <- lapply(1:max_table, function(i) {
          tablename <- paste("tablename", i, sep = "")
          tableOutput(tablename)
        })
        do.call(tagList, plot_output_list)
      })

      for (i in 1:max_table) {
        local({
          my_i <- i
          tablename <- paste("tablename", my_i, sep = "")
          output[[tablename]] <- renderTable({
            lst[[my_i]]
          })
        })
      }
    }
  })

}

shinyApp(ui = ui, server = server)

Upvotes: 3

Related Questions