Reputation: 84529
In some complex and large shiny apps, the UI parts are often rendered with renderUI
and uiOutput
. Consider this small app for illustration:
library(shiny)
modUI <- function(id) {
ns <- NS(id)
uiOutput(ns("theUI"))
}
modServer <- function(id) {
moduleServer(
id,
function(input, output, session) {
output[["plot"]] <- renderPlot({
n <- input[["numb"]]
plot(rnorm(n), rnorm(n), pch = 19L)
})
ns <- session$ns
output[["theUI"]] <- renderUI({
tagList(
sliderInput(ns("numb"), "N", 10, 100, 50),
plotOutput(ns("plot"))
)
})
}
)
}
ui <- basicPage(
br(),
modUI("myapp")
)
server <- function(input, output, session) {
modServer("myapp")
}
shinyApp(ui, server)
Here modUI
is ridiculously small. But in modServer
, the renderUI
could be large and there could be many other output
components. Therefore it is desirable to split modServer
:
renderPlotServer <- function(id) {
moduleServer(
id,
function(input, output, session) {
output[["plot"]] <- renderPlot({
n <- input[["numb"]]
plot(rnorm(n), rnorm(n), pch = 19L)
})
}
)
}
renderUIServer <- function(id) {
moduleServer(
id,
function(input, output, session) {
ns <- session$ns
output[["theUI"]] <- renderUI({
tagList(
sliderInput(ns("numb"), "N", 10, 100, 50),
plotOutput(ns("plot"))
)
})
}
)
}
and to put them together one can use the same id
:
modServer <- function(id) {
moduleServer(
id,
function(input, output, session) {
renderPlotServer("mysubapp")
renderUIServer("mysubapp")
}
)
}
but then we need a nested namespace in the UI part of the module:
modUI <- function(id) {
ns <- NS(NS(id)("mysubapp"))
uiOutput(ns("theUI"))
}
This is not convenient.
A more convenient solution consists in using a NULL
namespace, to avoid the nested namespace in the UI part:
modServer <- function(id) {
moduleServer(
id,
function(input, output, session) {
renderPlotServer(NULL)
renderUIServer(NULL)
}
)
}
But this solution has a problem: it prevent to use the module multiple times.
So, what would be a convenient solution avoiding this problem?
I think I have one: one can nest the "main" id with itself:
modServer <- function(id) {
moduleServer(
id,
function(input, output, session) {
renderPlotServer(id)
renderUIServer(id)
}
)
}
modUI <- function(id) {
ns <- NS(NS(id)(id))
uiOutput(ns("theUI"))
}
Do you have another nice solution?
Upvotes: 2
Views: 97
Reputation: 20329
So maybe I am overlooking some subtleties of your approach, but why would you need to use moduleServer
in render(Plot|UI)Server
in the first place? Rather you can go for a plain function and all the namespacing seems to work without any further ado?
library(shiny)
modUI <- function(id) {
ns <- NS(id)
uiOutput(ns("theUI"))
}
renderPlotServer <- function(input, output, session) {
output[["plot"]] <- renderPlot({
n <- input[["numb"]]
plot(rnorm(n), rnorm(n), pch = 19L)
})
}
renderUIServer <- function(input, output, session) {
ns <- session$ns
output[["theUI"]] <- renderUI({
tagList(
sliderInput(ns("numb"), "N", 10, 100, 50),
plotOutput(ns("plot"))
)
})
}
modServer <- function(id) {
moduleServer(
id,
function(input, output, session) {
renderPlotServer(input, output, session)
renderUIServer(input, output, session)
}
)
}
ui <- basicPage(
br(),
modUI("myapp"),
br(),
modUI("myapp2") ## a second module works like a charm
)
server <- function(input, output, session) {
modServer("myapp")
modServer("myapp2")
}
shinyApp(ui, server)
Upvotes: 1