Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to organize large Shiny apps?

Tags:

r

shiny

What are the best practices to organize larger Shiny applications?
I think best R practices are also applicable to Shiny.
Best R practices are discussed here: How to organize large R programs
Link to Google's R Style Guide: Style Guide

But what are the unique tips and tricks in Shiny context which I can adopt to make my Shiny code look better (and more readable)? I am thinking of things like:

  • Exploiting object oriented programming in Shiny
  • In server.R which parts should be sourced?
  • File hierarchy of project containing markdown documents, pictures, xml and source files

For example if I am using navbarPage and tabsetPanel in every tabPanel my code is starting to look quite messy after addition of several UI elements.

Example code:

server <- function(input, output) {   #Here functions and outputs..  }  ui <- shinyUI(navbarPage("My Application",   tabPanel("Component 1",              sidebarLayout(                 sidebarPanel(                     # UI elements..                 ),                 mainPanel(                     tabsetPanel(                         tabPanel("Plot", plotOutput("plot")                                  # More UI elements..                                  ),                          tabPanel("Summary", verbatimTextOutput("summary")                                  # And some more...                                  ),                          tabPanel("Table", tableOutput("table")                                  # And...                                  )                     )                 )     )              ),   tabPanel("Component 2"),   tabPanel("Component 3") ))  shinyApp(ui = ui, server = server) 

For organizing ui.R code I found quite nice solution from GitHub: radiant code
Solution is to use renderUI to render every tabPanel and in server.R tabs are sourced to different files.

server <- function(input, output) {    # This part can be in different source file for example component1.R   ###################################   output$component1 <- renderUI({         sidebarLayout(                 sidebarPanel(                 ),                 mainPanel(                     tabsetPanel(                         tabPanel("Plot", plotOutput("plot")),                          tabPanel("Summary", verbatimTextOutput("summary")),                          tabPanel("Table", tableOutput("table"))                     )                 )     )   })  #####################################    } ui <- shinyUI(navbarPage("My Application",   tabPanel("Component 1", uiOutput("component1")),   tabPanel("Component 2"),   tabPanel("Component 3") ))  shinyApp(ui = ui, server = server) 
like image 828
Mikael Jumppanen Avatar asked Nov 22 '14 17:11

Mikael Jumppanen


People also ask

How do you organize a Shiny app?

If you are creating a large or long-term Shiny app, I highly recommend that you organise your app in the same way as an R package. This means that you: Put all R code in the R/ directory. Write a function that starts your app (i.e. calls shinyApp() with your UI and server).

Where do you put libraries in the Shiny app?

Packages to load need to be declared at the start of the app. R file, at the server side of the app. R or alternatively at the modules file. At the modules, the libraries can be declared either outside of the fooUI and fooServer module functions or inside.

What can you do with Shiny apps?

Shiny is an R package that makes it easy to build interactive web apps straight from R. You can host standalone apps on a webpage or embed them in R Markdown documents or build dashboards. You can also extend your Shiny apps with CSS themes, htmlwidgets, and JavaScript actions.


1 Answers

After addition of modules to R shiny. Managing of complex structures in shiny applications has become a lot easier.

Detailed description of shiny modules:Here

Advantages of using modules:

  • Once created, they are easily reused
  • ID collisions is easier to avoid
  • Code organization based on inputs and output of modules

In tab based shiny app, one tab can be considered as one module which has inputs and outputs. Outputs of tabs can be then passed to other tabs as inputs.

Single-file app for tab-based structure which exploits modular thinking. App can be tested by using cars dataset. Parts of the code where copied from the Joe Cheng(first link). All comments are welcome.

# Tab module # This module creates new tab which renders dataTable  dataTabUI <- function(id, input, output) {   # Create a namespace function using the provided id   ns <- NS(id)    tagList(sidebarLayout(sidebarPanel(input),                          mainPanel(dataTableOutput(output))))  }  # Tab module # This module creates new tab which renders plot plotTabUI <- function(id, input, output) {   # Create a namespace function using the provided id   ns <- NS(id)    tagList(sidebarLayout(sidebarPanel(input),                          mainPanel(plotOutput(output))))  }  dataTab <- function(input, output, session) {   # do nothing...   # Should there be some logic?   }  # File input module # This module takes as input csv file and outputs dataframe # Module UI function csvFileInput <- function(id, label = "CSV file") {   # Create a namespace function using the provided id   ns <- NS(id)    tagList(     fileInput(ns("file"), label),     checkboxInput(ns("heading"), "Has heading"),     selectInput(       ns("quote"),       "Quote",       c(         "None" = "",         "Double quote" = "\"",         "Single quote" = "'"       )     )   ) }  # Module server function csvFile <- function(input, output, session, stringsAsFactors) {   # The selected file, if any   userFile <- reactive({     # If no file is selected, don't do anything     validate(need(input$file, message = FALSE))     input$file   })    # The user's data, parsed into a data frame   dataframe <- reactive({     read.csv(       userFile()$datapath,       header = input$heading,       quote = input$quote,       stringsAsFactors = stringsAsFactors     )   })    # We can run observers in here if we want to   observe({     msg <- sprintf("File %s was uploaded", userFile()$name)     cat(msg, "\n")   })    # Return the reactive that yields the data frame   return(dataframe) } basicPlotUI <- function(id) {   ns <- NS(id)   uiOutput(ns("controls"))  } # Functionality for dataselection for plot # SelectInput is rendered dynamically based on data  basicPlot <- function(input, output, session, data) {   output$controls <- renderUI({     ns <- session$ns     selectInput(ns("col"), "Columns", names(data), multiple = TRUE)   })   return(reactive({     validate(need(input$col, FALSE))     data[, input$col]   })) }  ################################################################################## # Here starts main program. Lines above can be sourced: source("path-to-module.R") ##################################################################################  library(shiny)   ui <- shinyUI(navbarPage(   "My Application",   tabPanel("File upload", dataTabUI(     "tab1",     csvFileInput("datafile", "User data (.csv format)"),     "table"   )),   tabPanel("Plot", plotTabUI(     "tab2", basicPlotUI("plot1"), "plotOutput"   ))  ))   server <- function(input, output, session) {   datafile <- callModule(csvFile, "datafile",                          stringsAsFactors = FALSE)    output$table <- renderDataTable({     datafile()   })    plotData <- callModule(basicPlot, "plot1", datafile())    output$plotOutput <- renderPlot({     plot(plotData())   }) }   shinyApp(ui, server) 
like image 58
Mikael Jumppanen Avatar answered Oct 05 '22 01:10

Mikael Jumppanen