2

Is there any way to display multiple tables in Rshiny when the number of tables changes case by case? I want to create a Rshiny app which can read multiple csv files and display multiple tables corresponding to each csv file. However, the number of files provided will be different case by case.

The code I have written so far can read multiple files and store them in reactive values list but I don't know what the app should do in term of the output. here is my codes:

ui <- fluidPage(
  sidebarLayout(
    sidebarPanel(
      fileInput("upload", "Upload a file", multiple=TRUE)
      ),
    mainPanel(
      tableOutput("table")
      )
    )
)

server <- function(input, output, session) {
  data <- reactive({
    req(input$upload)
    files <- reactiveValues()
    browser()
    for (i in 1:length(input$upload$datapath)){
      files[[paste0(i)]] <- read.csv(input$upload$datapath[i]) 
    }
    return(files)
  })
  
  output$table <- renderTable({
    data()
  })
}

shinyApp(ui, server)```
fogo000
  • 21
  • 1
  • 2
    This is a great application of [shiny modules](https://shiny.rstudio.com/articles/modules.html). Basically you build a ui/server module pair that displays a dataset and then `lapply()` your module over the list of input files. – Dan Adams Aug 11 '22 at 01:02
  • I answered a similar question [here](https://stackoverflow.com/questions/73122737/r-shiny-allow-user-to-select-one-or-multiple-datasets-to-download/73123705#73123705) that shows how to use shiny modules to display a variable number of tables – gdevaux Aug 11 '22 at 07:20

1 Answers1

0

As suggested in the comments modules are a solution:

library(shiny)
library(DT)
library(uuid)

tmpdir <- tempdir()
cat(tmpdir)
write.csv(mtcars, file.path(tmpdir, "mtcars.csv"))
write.csv(iris, file.path(tmpdir, "iris.csv"))


csv_ui <- function(id) {
  ns <- NS(id)
  fluidRow(
    DTOutput(ns("tab"))
  )
}

csv_server <- function(id, the_data) {
  moduleServer(
    id,
    function(input, output, session) {
      output$tab <- renderDataTable({
        datatable(the_data)
      })
    }
  )
}

ui <- fluidPage(
  titlePanel("Modules to the Rescue"),
  sidebarLayout(
    sidebarPanel(
      fileInput("upload", "Upload a file", multiple = TRUE)
    ),
    mainPanel(
      uiOutput("tables")
    )
  )
)

server <- function(input, output, session) {
  obs <- list()
  output$tables <- renderUI({
    paths <- req(input$upload$datapath)
    tabs <- vector("list", length(input$upload$datapath))
    for (idx in seq_along(paths)) {
      local({
        jdx <- idx
        uid <- UUIDgenerate() ## or create any other unique name
        tabs[[jdx]] <<- csv_ui(uid)
        obs[[jdx]] <<- csv_server(uid, read.csv(input$upload$datapath[jdx]))
      })
    }
    do.call(tagList, tabs)
  })
}

shinyApp(ui, server)

You need the local construct, because otherwise the last table will be printed twice. This has to do with lazy evaluation and a nice comparison can be found in this gist

thothal
  • 16,690
  • 3
  • 36
  • 71