Search code examples
rshinyshinydashboardshinyapps

Add number of fileInputs based on the value of a selectInput with modularised Shiny code


Apologies for the lack of information or example, I am lost, especially with the number of Stack Overflow questions that use the now unrecommended callModule(). I imagine I would need nested modules, where there is a ui module which houses the selectInput, which allows the user to select the number of fileInputs. The value of the selectInput would then be fed to a uiOutput, which would somehow create the fileInputs in the same namespace as the selectInput. In the app, the fileInputs would appear directly below the selectInput. Does this sound alright? Any guidance or examples I haven't been able to find are appreciated! :)

I tried some approaches where the fileInputs did not show, but there was no error code. Deleted out of frustration :) Attempted start below:

library(shiny)
library(shinydashboard)
library(shinydashboardPlus)


outerUI <- function(id, multiple = FALSE){
  ns <- NS(id)
  fileTagList <- tagList(selectInput(ns("compare"), "Compare",
                                     choices = 2:4, selected = 2))
  ## and something in here???

}

outerServer <- function(id) {
  moduleServer(id, function(input, output, session) {
    
  }
  )
}

innerUI <- function(id, multiple = FALSE){
  ns <- NS(id)
  for(i in 1:input$ncompare){
    fileTagList <- c(fileTagList,
                     tagList(tagList(fileInput(ns(paste0("pf",i)),
                                               paste0("Input",i)))))
  }
  
}

innerServer <- function(id) {
  moduleServer(id, function(input, output, session) {
    
  }
  )
}

server <- function(input, output) {
  outerServer("test")
}

ui <- dashboardPage(
  dashboardHeader(),
  dashboardSidebar(
    sidebarMenu(
      menuItem("tab1", tabName = "tab1")
    )),
  dashboardBody(
    tabItem(tabName = "home",
            outerUI("test")
    )
  )
)

shinyApp(ui, server)

Solution

  • You were on the right path (uiOutput + renderUI). Here is a reprex.

    dynamic number of fileinputs demo

    library(shiny)
    
    mod_inputs_ui <- \(id) {
      ns <- NS(id)
      
      tagList(
        selectInput(
          inputId = ns("selector"),
          label = "How many file inputs?",
          choices = 1:5
        ),
        uiOutput(outputId = ns("file_inputs"))
      )
    }
    
    mod_inputs_server <- \(id) {
      moduleServer(
        id = id,
        module = \(input, output, session) {
          ns <- session$ns
          
          output$file_inputs <- renderUI({
            req(input$selector)
            
            lapply(seq_len(input$selector), \(i){
              fileInput(
                inputId = ns(paste0("file_input_", i)),
                label = paste("File Input", i)
              )
            })
          }) |> bindEvent(input$selector)
        }
      )
    }
    
    ui <- fluidPage(
      tags$h1("Dynamic number of fileinputs"),
      mod_inputs_ui("fileinputs")
    )
    
    server <- \(input, output, session) {
      mod_inputs_server("fileinputs")
    }
    
    shinyApp(ui, server)