How to organize large Shiny apps?

前端 未结 4 1682
无人共我
无人共我 2020-12-12 09:32

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

相关标签:
4条回答
  • 2020-12-12 09:50

    I wrote Radiant. I have not heard people say bad things about the code organization (yet) but I am sure it could be better. One option would be to separate the ui and logic as Joe Cheng does in shiny-partials.

    https://github.com/jcheng5/shiny-partials

    Another might be to try OO programming, e.g., using R6 http://rpubs.com/wch/17459

    0 讨论(0)
  • 2020-12-12 09:50

    Now there is also the golem package that provides a framework for organising shiny code. It mainly uses modules, but also provides a structure for how to organise e.g. helper functions and css/javascript files. There is also an accompanying book.

    0 讨论(0)
  • 2020-12-12 10:04

    I really like how Matt Leonawicz organises his apps. I took his approach learning how to use Shiny, as we all know it can get quite scattered if not properly managed. Have a look at his structure, he gives an overview of the way he organises the apps in the app called run_alfresco

    https://github.com/ua-snap/shiny-apps

    0 讨论(0)
  • 2020-12-12 10:05

    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)
    
    0 讨论(0)
提交回复
热议问题