rshinyshinymodulesrenderui

Passing Plots from inside a renderUI in a Shiny Module to main Server


I am trying to understand how to pass information from a module to the main server of a Shiny App. This is an oversimplification of my actual code, so I understand that it could be done in a different way, but I need to do this primarily with callModule in the server.R file.

# Mod1.R File
modUI <- function(id) {

ns <- NS(id)

  tagList(
    fluidRow(
      column(
        width = 12,
        numericInput(ns("num"), "Choose a number to plot", value = 3),
        uiOutput(ns("bins"))
      )
    )
  )
}

modServer <- function(input, output, session) {
  
  ns <- session$ns
  
  output$bins <- renderUI(
    ns <- session$ns,
    selectInput(ns("plot_type"), "select plot", c("hist", "plot")),
    plotOutput(ns("plott"))
  )
  
  output$plott <- renderPlot(
    if (input$plot_type == "hist"){
      hist(input$num)
    } else (
      plot(input$num)
    )
  )

}

##############

# App.R File

library(shiny)
library(tidyverse)

# Modules
source("mod1.R")

    # Main App ----------------------------------------------------------------
    
    ui <- fluidPage(
      modUI("ssss")
    )  # Fluid Page
    
    
    server <- function(input, output, session) {
      callModule(modServer, "ssss")
    }
    
    
    shinyApp(ui, server)

I'm trying to return the plot that should be generated inside the Mod1.R file to the App.R file in the server function, but I'm not quite sure how to do this. I know I should return a reactive output like: return(reactive(output$plott)) in the Mod1.R file, but this doesn't do anything. Can you please guide me in the right direction? Thanks.


Solution

  • I'm not sure what you mean by "return the plot .... to the app". If all you want to do is display the plot, then this seems to fix the problems in your code:

    # Mod1.R File
    modUI <- function(id) {
      
      ns <- NS(id)
      
      tagList(
        fluidRow(
          column(
            width = 12,
            numericInput(ns("num"), "Choose a number to plot", value = 3),
            uiOutput(ns("bins"))
          )
        )
      )
    }
    
    modServer <- function(input, output, session) {
      
      ns <- session$ns
      
      output$bins <- renderUI({
        tagList(
          selectInput(ns("plot_type"), "select plot", c("hist", "plot")),
          plotOutput(ns("plott"))
        )
      })
      
      output$plott <- renderPlot(
        if (input$plot_type == "hist"){
          hist(input$num)
        } else (
          plot(input$num)
        )
      )
      
    }
    
    ##############
    
    # App.R File
    
    library(shiny)
    library(tidyverse)
    
    # Modules
    
    # Main App ----------------------------------------------------------------
    
    ui <- fluidPage(
      modUI("ssss")
    )  # Fluid Page
    
    
    server <- function(input, output, session) {
      callModule(modServer, "ssss")
    }
    
    
    shinyApp(ui, server)
    

    If you genuinely want to return the plot rather than simply display it, then you'd need to create a reactive containing the plot outside of your output$plott reactive and then return that reactive (not its value) from the module UI. Something like:

    modServer <- function(input, output, session) {
      
      ns <- session$ns
      
      output$bins <- renderUI({
        tagList(
          selectInput(ns("plot_type"), "select plot", c("hist", "plot")),
          plotOutput(ns("plott"))
        )
      })
      
      myPlot <- reactive({
        if (input$plot_type == "hist"){
          hist(input$num)
        } else (
          plot(input$num)
        )
      })
      
      output$plott <- renderPlot({
        myPlot()
      })
      
      return(myPlot)
    }
    

    and

    server <- function(input, output, session) {
      mainServerPlot <- callModule(modServer, "ssss")
    }
    

    You can then reference the plot object returned by the module with mainServerPlot() within the main server.