To me, the easiest way to solve this problem is to create a module that will manage a single plot and then create the required number of instances of the module in the main server function. You can read more about Shiny modules here.
A Shiny module consists of two functions, a UI function and a server function. These are paired by the fact that they share a common ID. The ID is used to distinguish different instances of the same module. Namespacing (the ns function) is used to distinguish instances of the same widget in different instances of the module.
The module UI function is straightforward. It simply creates a plotOutput:
plotUI <- function(id) {
ns <- NS(id)
plotOutput(ns("plot"))
}
The module server function takes three parameters: an id and the names of the x and y variables to plot.
plotServer <- function(id, Xvar, Yvar) {
moduleServer(
id,
function(input, output, session) {
output$plot <- renderPlot({
req(Xvar)
mtcars %>%
ggplot(aes(get(Xvar), get(Yvar))) +
geom_point() +
labs(x = Xvar, y = Yvar)
})
}
)
}
The main UI function creates the sidebar menu (there's no need for a Plot actionButton as Shiny's reactivity makes sure everything gets updated at the correct time) and a main panel that consists only of a uiOutput.
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
selectizeInput(inputId = "x",label = "X", choices = names(mtcars), multiple = T),
selectInput(inputId = "y",label = "Y", choices = names(mtcars), multiple = F)
),
mainPanel(
uiOutput("plotUI")
)
)
)
The main server function is where the magic happens. Every time there's a change to input$x or input$y, new instances of the module UI and server functions are created. One for each selection in input$x. The id for each module is simply an integer. The appropriate column names are passed to each instance of the module server function. A call to renderUI creates the UI for each instance of the module.
server <- function(input, output, session) {
output$plotUI <- renderUI({
ns <- session$ns
tagList(
lapply(1:length(input$x),
function(i) {
plotUI(paste0("plot", i))
}
)
)
})
observeEvent(c(input$x, input$y), {
plotServerList <- lapply(
1:length(input$x),
function(i) {
plotServer(paste0("plot", i), input$x[i], input$y)
}
)
})
}
Putting it all together:
library(dplyr)
library(ggplot2)
library(shiny)
# Plot module UI function
plotUI <- function(id) {
ns <- NS(id)
plotOutput(ns("plot"))
}
# Plot module server function
plotServer <- function(id, Xvar, Yvar) {
moduleServer(
id,
function(input, output, session) {
output$plot <- renderPlot({
req(Xvar)
mtcars %>%
ggplot(aes(get(Xvar), get(Yvar))) +
geom_point() +
labs(x = Xvar, y = Yvar)
})
}
)
}
# Main UI
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
selectizeInput(inputId = "x",label = "X", choices = names(mtcars), multiple = T),
selectInput(inputId = "y",label = "Y", choices = names(mtcars), multiple = F)
),
mainPanel(
uiOutput("plotUI")
)
)
)
# Main server
server <- function(input, output, session) {
output$plotUI <- renderUI({
ns <- session$ns
tagList(
lapply(1:length(input$x),
function(i) {
plotUI(paste0("plot", i))
}
)
)
})
observeEvent(c(input$x, input$y), {
plotServerList <- lapply(
1:length(input$x),
function(i) {
plotServer(paste0("plot", i), input$x[i], input$y)
}
)
})
}
shinyApp(ui, server)
