Shiny module design patterns: Pass a single input to multiple modules

For the awesome Shiny Developers Conference back in January, I endeavoured to learn about shiny modules and overhaul an application using them in the space of two days. I succeeded and almost immediately switched onto other projects, thereby losing most of the hard-won knowledge! As I rediscover shiny modules and start putting them into more active use, I’ll be blogging about design patterns. This post takes you through the case of multiple modules receiving the same input value.

TL;DR

Stick overall config input objects at the app level and pass them in a reactive expression to callModule(). Pass the results in as an extra argument into subsequent modules. These are reactive so don’t forget the brackets. Steal the code and, as always, if you can improve it do so!

Starting out

For the purposes of this post, I’ll be morphing the dummy application that Rstudio produces when you use New file > Shiny app. I’ve created a repository to store these design patterns in and the default shiny app that will be converted / mangled is the 01_original.R file.

input value being directly used in shiny app

input value being directly used in shiny app

Pass a single input to multiple modules

Make a reactive value

The first thing you might assume you can do when you want to pass an input value to a module is simply do callModule(chart,"chartA",input$bins). Unfortunately, this does not work because the callModule() function is not inherently reactive. it has to be forced to be reactive with a reactive value.

We can make a reactive value very simply:

bins<-reactive({ input$bins })

Make a module that accepts additional arguments

The vanilla module server function construct is function(input,output,session){}. There isn’t room for extra parameters to be passed through so we have to make space for one. In this case, our module skeleton that will hold our histogram code is

charts <- function( input, output, session, bins) {

}

To pass through our reactive value then becomes

bins<-reactive({ input$bins })
callModules(charts, "chartA", bins)

Use the reactive value

When you reference a reactive value, you reference it like a function. We need to use bins() instead so that the result of the reactive value is returned.

Instead of bins <- seq(min(x), max(x), length.out = input$bins + 1) in our original, when we use our reactive value in our chart module, it becomes:

chart <- function(input, output, session, bins) {
  output$distPlot <- renderPlot({
    x    <- faithful[, 2]
    bins <- seq(min(x), max(x), length.out = bins() + 1)
    hist(x,
         breaks = bins,
         col = 'darkgray',
         border = 'white')
  })
}

Putting it together

To be able to pass an input value to a module, you need to:

  1. Make a reactive variable holding the input value
  2. Add an argument to your module’s server function arguments
  3. Pass the name of the reactive variable to the module
  4. Use argument() not argument within the module’s server function main code

See file 02_singlegloballevelinput.R for the end to end solution.

Store input in a reactive value to pass value onto modules

Store input in a reactive value to pass value onto modules


Further study

Search