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.
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:
- Make a reactive variable holding the input value
- Add an argument to your module’s server function arguments
- Pass the name of the reactive variable to the module
- Use
argument()
notargument
within the module’s server function main code
See file 02_singlegloballevelinput.R for the end to end solution.
Further study
- My post: Declutter a shiny report’s code v2.0
- Shiny Modules Rstudio article
- Understanding Modules webinar and associated materials
- Modularizing Shiny app code video from Shiny Developer Conference