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)

推荐答案

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)

R相关问答推荐

如何使下一个按钮只出现在Rshiny 的一段时间后?""

提取具有连续零值的行,如果它们前面有R中的有效值

IMF IFS数据以R表示

R -在先前group_by级别汇总时获取最大大小子组的计数

R -使用矩阵reshape 列表

R如何将列名转换为更好的年和月格式

有没有办法将不等长的列表转换为R中的数据帧

如何在使用因子时获得Sankey图的Scale_Fill_Viridis的全范围

我已经运行了几个月的代码的`Palette()`中出现了新的gglot错误

使用一个标签共享多个组图图例符号

在R中添加要打印的垂直线

从字符串01JAN2021创建日期

如何在基数R中根据矩阵散点图中的因子给数据上色?

如何从r中最早的公共月-日开始,在不同年份计算不同组中相同数量的值?

如何使用str_Detect计算并返回列表中检测到的字符串?

在ggplot中创建一个折线图,根据百分比的变化,线条采用不同的 colored颜色

在一列中创建 colored颜色 标签,在另一列中创建值,图例将两者结合在一起

如何在Rmarkdown中保存绘图?

LME4样本量和完整病例数据集之间是否存在差异?

迭代筛选器列表