Continuing in the series of shiny module design patterns, this post covers how to pass all the inputs from one module to another.

TL;DR

Return input from within the server call. Store the callModule() result in a variable. Pass the variable into arguments for other modules. Access the variable like you would input. 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 in each folder. This post covers inputs being passed on to other modules so is held in the input_to_multiplemodules folder.

input value being directly used in shiny app
input value being directly used in shiny app

Pass module inputs to other modules

Make a module that contains inputs

A module must have a server function and can optionally have UI and Input functions. We need a module that has an input function so our skeleton for our setup values is

setupInput <- function( id ){
ns<-NS(id)

}

Input parameters must be held in a tagList() so that the shiny UI knows to handle them like other directly mentioned inputs. The setupInput function can contain any number of inputs, including the bins input that used to be a global input.

setupInput<-function(id){
  ns<-NS(id)
  tagList(
  sliderInput(ns("bins"), "Number of bins:",
              min = 1,  max = 50, value = 30)
  )
}

Output the inputs

In the module with the input parameters, we may need to do stuff with the value and also make it available for other modules. To make the input parameters available, we need to put the input object in the return() value of the module.

setup<-function(input,output,session){
  return(input)
}

In the example file, I also show how to show all inputs for a module in a table. This is very handy for providing a consolidated list of parameters that could be viewed or exported. The input object is a reactive R6 object and cannot be directly coerced into a data.frame. To enable the coercion the reactiveValuesToList() function needs to be used first. This, of course, can be applied to any reactive value making it easy to extract values where required.

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 enable us to utilise the results of the callModule(setup,"setupA") we need to store the results, and then pass through the object.

bins<-callModule(setup,"setupA")
callModules(charts, "chartA", bins)

Use the input value

When you reference a reactive value, you reference it like a function but what we’ve passed through is an input object. As a result we don’t need to use bins() just bins and we can use it just like it was input so we can then use bins$bin.

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$bin + 1)
    hist(x,
         breaks = bins,
         col = 'darkgray',
         border = 'white')
  })
}

Note that the input values did not receive a namespace so could be labelled directly. If you run the sample file for this design pattern, you’ll note that the printing of the input values did print the namespaces as the UI output component utilised the ns() function.

Putting it together

To be able to pass all inputs from one module to another, you need to:

  1. Make a module that returns the `input` object
  2. Store the callModule results in a variable
  3. Add an argument to your module’s server function arguments
  4. Pass the name of the variable to the module
  5. Use argument$inputparamname within the module’s server function main code to refer to a specific input parameter

See file 04_allinputsmodule.R for the end to end solution.

Store input in a reactive value to pass values onto modules
Store callModule to pass into other modules

Further study

Shiny module design patterns: Pass module inputs to other modules
Tagged on:                 

3 thoughts on “Shiny module design patterns: Pass module inputs to other modules

  • 19/04/2016 at 23:23
    Permalink

    Nice post Steph. I havent seen much posted online beyond the initial Rstudio module release walkthrough (which is very good) on modules in Shiny. I have spent a lot of time extending out modules to build some very big, complex apps, nesting modules inside modules etc.

    I figure there are not a ton of people working with modules in depth so wanted to say hi, nice blog and keep in touch.

    Reply
    • 20/04/2016 at 10:07
      Permalink

      Hiya Trevor, great to hear from you! Yeah the dearth of info out there makes it quite tough for people to get started. I’m working my way through but I’m very open to contributors if you’d like to speed the process along – there’s a lot of patterns like nested modules and it would probably be sensible long term to not have these as individual blog posts but build up the repository with them instead. If you’re up for adding some samples, let me know 😀

      Reply
  • 26/12/2016 at 13:14
    Permalink

    “…the dearth of info out there makes it quite tough for people to get started.”

    Garrett’s webinar (https://www.rstudio.com/resources/videos/modularizing-shiny-app-code/) was — as usual — a great kick-start for me.

    Joe’s RStudio article on modules (http://shiny.rstudio.com/articles/modules.html) was very informative and answered a lot of Qs I had.

    This present article provided essential help as I re-factor an app that requires modules to play nice with the shinyStore package (https://github.com/trestletech/shinyStore).

    Thanks for this, Steph.

    Reply

What do you think?