I want to make an RShiny app where the user can select a folder, and then add a navbarMenu
containing as many tabPanel
s as there are files in the selected folder. The tabPanel
s are made with a module. Here is an app with a button that adds just one tab with one call to the module:
library(shiny)
mod_tabPanel_ui <- function(id, title) {
ns <- NS(id)
tabPanel(
title = title,
fluidPage(
h1("Module UI goes here")
)
)
}
mod_tabPanel_server <- function(id) {
moduleServer(id, function(input, output, session) {
ns <- session$ns
})
}
ui <- navbarPage(
title = "Main Title",
id = "tabs",
tabPanel(
id = "home",
title = "Home",
actionButton(
inputId = "add_tab",
label = "Add tab"
)
)
)
server <- function(input, output, session) {
## other server logic goes here
observeEvent(input$add_tab, {
appendTab(
inputId = "tabs",
navbarMenu(
title = "new tab",
mod_tabPanel_ui("mod1", "Module 1")
)
)
mod_tabPanel_server("mod1")
})
}
shinyApp(ui, server)
The add_tab actionButton
makes a new navbarMenu
using the appendTab
function with one call to the module mod_tabPanel_ui
+mod_tabPanel_server
.
Now what I want to be able to do is something like this, where the 1:3
in the for loop will be replaced with 1:number of files
:
server <- function(input, output, session) {
## other server logic goes here
observeEvent(input$add_tab, {
appendTab(
inputId = "tabs",
navbarMenu(
title = "new tab",
# mod_tabPanel_ui("mod1", "Module 1")
for (i in 1:3) {
mod_tabPanel_ui(paste("mod1", i), paste("Module", i))
}
)
)
for (i in 1:3) {
mod_tabPanel_server(paste("mod1", i))
}
})
}
But this results on the module never appearing in the new tabs.
I've been told that it is possible to do by using assign
and environment
like so:
server <- function(input, output, session) {
shiny_env <- environment()
## other server logic goes here
observeEvent(input$add_tab, {
appendTab(
inputId = "tabs",
navbarMenu(
title = "new tab",
lapply(1:3, function(i) {
mod_tabPanel_ui(paste("mod1", i), paste("Module", i))
})
)
)
for (i in 1:3) {
assign(paste0("dynamic_server_", i),
mod_tabPanel_server(paste0("exp", i)),
envir = shiny_env)
}
})
}
But this give the error message:
Warning: Error in : Navigation containers expect a collection of
bslib::nav()
/shiny::tabPanel()
s and/orbslib::nav_menu()
/shiny::navbarMenu()
s. Consider usingheader
orfooter
if you wish to place content above (or below) every panel's contents.
Which does not really help me. Reading the observeEvent documentation makes me think that the problem is that the module is being created in the wrong environment of something along those lines.
Got it working with
do.call
andlapply
: