Phil D
Phil D

Reputation: 303

Placing Shiny render outputs inside other functions

So I am making a shiny app that takes multiple inputs and produces multiple plots and tables of those inputs.

Sample RenderTable code to make one table is below:

output$Table1 <- 
      renderTable(data.frame(Shape = c("\u25CF", "\u25B2", "\u25FC", ""),
                             "Genes" = c(input$custom_genes1, "Mean"),
                              Analysed_values = c(t(analysis1()), mean(analysis1()))), 
                              striped = TRUE, bordered = TRUE, hover = TRUE)

Where Shape is the symbol on the plot, "Genes" are the genes selected using a selectizeInput, and analysis1() is the result of a reactive calculation defined earlier in the code.

However I have 8-9 tables that need to be displayed depending on various checkboxes etc. Rather than copy-pasting the code out and replacing all the relevant values, I wanted to make a function that I could use to quickly display all the tables, and if I needed to change the formatting or add anything later I can just edit the function rather than each table individually.

I tried making a re-usable function using the code below:

construct_table <- function(genes_select, dataset){
    renderTable(data.frame(Shape = c("\u25CF", "\u25B2", "\u25FC", ""),
                          "Genes" = genes_select,
                           Analysed_values = c(t(dataset), mean(dataset))), 
                           striped = TRUE, bordered = TRUE, hover = TRUE)
  }

And then making it easier to call each table directly:

output$Table1 <- construct_table(input$custom_genes1, analysis1())

This works in that is creates the table using the initial input values, but doesn't make the table reactive.

How can I call the renderTable reactive function inside a normal function, or is there another reactive function that I have overlooked that I can nest the renderTable function inside of that works the same way as the normal function does?

Upvotes: 0

Views: 1025

Answers (1)

Sada93
Sada93

Reputation: 2835

Simply adding a output$Table1 <- construct_table(input$custom_genes1, analysis1()) call anywhere will not work because analysis() is a reactive variable depending on other inputs the user can change. Hence analysis() can be used from a reactive context. ie. from inside observe, observeEvent, reactive, eventReactive or inside any renderXXX call ex: renderTable, renderPlot...

When we call output$Table1 <- construct_table(input$custom_genes1, analysis1()), we are using analysis1() from outside a reactive context which will cause an error. The solution is Shiny Modules which is specifically designed to modularize code, which seems to be the goal here.

https://shiny.rstudio.com/articles/modules.html

Here is an example,

dataset = mtcars

generatePlot = function(input,output,session,data){
  output$plot = renderPlot({
    plot(data()$hp,data()$mpg)
    })
}


generateUI = function(id){
  ns = NS(id)
  plotOutput(ns("plot"))
}

shinyApp(
  ui = {
    fluidPage(
      sliderInput("slider","MPG filter",min(dataset$mpg),max(dataset$mpg),dataset$mpg),
      generateUI("mod1"),
      generateUI("mod2")
    )
  },
  server = function(input,output,session){

    data = eventReactive(input$slider,{
      dataset%>%
        dplyr::filter(between(mpg,input$slider[[1]],input$slider[[2]]))%>%
        return(.)
    })

    callModule(generatePlot,"mod1",data = data)
    callModule(generatePlot,"mod2",data = data)

  }
)

You can add many more identical plots by simply adding a new generateUI and a new callModule call.

Upvotes: 2

Related Questions