Update April 2016: RStudio now has an official way to write composable shiny "modules".
Shiny is a library for writing web applications in R. If you use R, I highly recommend it, it's very clever. If you haven't used R and Shiny, this blog post is going to be pretty incomprehensible.
I wanted a way to compose Shiny apps. I haven't found an official way to do this, so this post details my solution. Maybe some day the clever people at RStudio will release an official way that makes this all moot.
You may want to review the tutorial on the various ways a Shiny app may be constructed and run. I will be constructing Shiny apps using the shinyApp function. This can either be done interactively, or from a file "app.R" in a Shiny app directory. Working interactively, an app is launched when it is print()ed, similar to ggplot. So, working interactively, calling a function to create a shiny app will launch it immediately (unless you save it to a variable). Only in R!
You may also want to review how reactive expressions in Shiny work. Recall that it is the job of the server function in a Shiny app to create all the reactive expressions
My code can be found in my Varistran package, in particular the file shiny_util.R.
What I call a "composable Shiny app" is a function in R returning a Shiny app object (so it can be used in the normal ways), but with that app object having some extra fields, $component_ui containing the UI part of the app and $component_server containing the server function.
varistran::shiny_plot is an example of a composable Shiny app. Such a function constructs ui and server and then calls varistran::composable_shiny_app(ui,server) to create the app.
Considerations when writing a composable Shiny app:
- To avoid namespace collisions, the app should be able to give names used in the input and output objects a specified prefix.
- To provide output usable by other apps, the server function needs somewhere to store reactive expressions. Therefore composable_shiny_app(ui,server) wraps the server function in code that creates an R environment (ie the type in R that is mutable) and passes this to the server function. This is the only argument to the server function, the usual parameters input and output are placed inside the environment.
- To use output from other apps, arguments to the function should be allowed to themselves be functions. I call these "reactables". These take a single argument, the environment, and hence are able to access reactive expressions that have been placed in the environment.
- An app incorporating other apps needs to call their server function in its own. This ensures all the apps have a chance to set up their outputs and place reactive expressions in the environment.
Example
This is all a bit confusing. Reading over the varistran::shiny_plot function should give you a better idea of how it all fits together. You will note in this function:
- The argument "callback" should be a function taking env as an argument, and producing a plot. Hence this plot might be based on inputs and reactive expressions from elsewhere.
- All of the inputs and outputs have a prefix pasted to the front of them.
- The return value is produced by the function varistran::composable_shiny_app. This means is it can be used immediately by printing out the value, or in the app.R of a Shiny app directory, or as a component in a larger app.
Example usage:
install.packages(c("devtools", "shiny")) devtools::install_github("MonashBioinformaticsPlatform/varistran") library(shiny) plot_app <- varistran::shiny_plot( function(env) { plot((1:10)**env$input$power) }, prefix="myplot" ) ui <- fluidPage( titlePanel("Composable app demo"), numericInput("power", "Power", 2), plot_app$component_ui ) server <- function(env) { plot_app$component_server(env) } varistran::composable_shiny_app(ui, server)
The plot component is able to react to changes from outside, namely the value of input$power.