Hi all,
I'm trying to do something that initially appeared quite simple: use a user input that, in the end, uses one or the other module to generate UI and server (and data). Is it actually possible in shiny?
I'm having the hardest of times of synchronizing everything. The module servers give output, I need to call them somewhere. But :
- if it is outside of the same consumer that also holds the
renderUI
part, then a module server may exist without a module UI, moreover, the synchronicity of the UI and server modules are not always assured; - if it is in the same consumer, then I can't seem to make them work together, as the consumer is not executed often enough.
I tried crafting a small example (such a factorization does not make sense for a simple app like that, but does in my project). This app has two modules, one for each distribution (normal and uniform), that returns a vector in the end (rnorm
or runif
). The app only does the plotting :
library(shiny)
# Uniform module
uniUI <- function(id) {
ns <- NS(id)
tagList(
numericInput(
inputId = ns("min"),
label = "choose the min of the distribution",
value = 0,
min = 0,
max = 10
),
numericInput(
inputId = ns("max"),
label = "choose the max of the distribution",
value = 10,
min = 10,
max = 20
)
)
}
uniServer <- function(id) {
moduleServer(id, function(input, output, session) {
res <- reactive({
req(input$min,
input$max)
runif(100, input$min, input$max)
})
res
})
}
# normal module
normalUI <- function(id) {
ns <- NS(id)
numericInput(
inputId = ns("mean"),
label = "choose the mean of the distribution",
value = 1,
min = 0,
max = 10
)
}
normalServer <- function(id) {
moduleServer(id, function(input, output, session) {
res <- reactive({
req(input$mean)
rnorm(100, input$mean, 2)
})
res
})
}
# App
ui <- fluidPage(
selectInput("choose", "Choose method", c("normal", "uniform")),
uiOutput("choice"),
plotOutput("plot")
)
server <- function(input, output, session) {
output$choice <- renderUI({
if(input$choose == "normal") {
normalUI("norm")
} else {
uniUI("uni")
}
})
data <- reactive({
if(input$choose == "normal") {
norm_res <- normalServer("norm")
norm_res()
} else {
uni_res <- uniServer("uni")
uni_res()
}
})
output$plot <- renderPlot({
req(data())
hist(data())
})
}
shinyApp(ui, server)
Here I tried option 1, but option 2 also does not work. This example actually works as intended some of the time, but not all of it, which is another head scratcher. In my bigger project, it does not work at all.
Initially I managed to make my project work, using the Dynamic visibility trick described here in Mastering Shiny, but type = "hidden"
is not a tabPanel
argument supported by bs4dash
, that I would like to use as well.
It solved the aforementioned issue, as both modules' UIs were actually existing, one was just hidden.
Is there a way not to use this option, while still having dynamic rendering of module UIs?
Edit: I managed to solve my problem with conditionalPanel()
, whose existence I had forgotten. But is there no way to use renderUI()
/uiOutput()
to do the same thing?