Jenna Hamlin
Jenna Hamlin

Reputation: 45

module for inputting csv/tsv/txt files in rshiny

I am developing a R Shiny application which will rely on a module in hopes that I can re-use the module for uploading and displaying two different data sets. As of now, my code works but I think I could make it a little bit cleaner as I don't think I have gotten the module correct. By that I mean, how do I move this code snippet (below) out of the app_server and into the module server and then use the callModule function for two different datasets. Similarly, I probably need to remove this code: tableOutput("metacontent") from the app ui and have that call in the module ui. See the module ui, module server, app ui, and app server below code snippet. Any suggestions? Thanks!

#code snippet
output$metacontents <- renderTable({
    metafile()

  })
# Module UI
mod_dataInput_ui <- function(id, label) {
  # Create a namespace function using the provided id
  ns <- NS(id)

  tagList(

    # Input: Select a file ----
    fileInput(ns("id"), label, 
              multiple = FALSE,
              accept = c("text/csv", 
                         "text/comma-separated-values,text/plain",
                         ".csv",
                         ".tsv")),

    # Input: Select separator ----
    radioButtons(ns("sep"), "Separator",
                 choices = c(Comma = ",",
                             Tab = "\t"),
                 selected = "\t"))
}

# Module Server
mod_dataInput_server <- function(input, output, session) {

  userFile <- reactive({
    validate(need(input$id !="", "Please import a data file"))
    input$id
  })    

  datafile <- reactive({
    utils::read.table(userFile()$datapath,
                      header = FALSE,
                      sep = input$sep,
                      row.names = NULL,
                      skip = 1,
                      stringsAsFactors = FALSE)

  })

}
#App UI
app_ui <- function() {
  tagList(
    # Leave this function for adding external resources
    golem_add_external_resources(),
    # List the first level UI elements here 
    navbarPage("Tinsel",
               tabPanel("Load Data",
                        sidebarPanel(mod_dataInput_ui("dataInput_ui_meta", tags$div("User META data", tags$br(), "(.csv, .tsv, or .txt file format)")), helpText("Can add help text here"),
                                     # Horizontal line ----
                                     tags$hr(style="border-color: black;"),
                                     mod_dataInput_ui("dataInput_ui_gene", tags$div("User GENETIC data", tags$br(), "(.csv, .tsv, or .txt file format)")),
                                     tags$hr(style="border-color: black;")),
                        mainPanel(
                          tabsetPanel(
                            tabPanel("Meta Data",
                                     tableOutput("metacontents")),
                            tabPanel("Genetic Data",
                                     tableOutput("genecontents"))
                          )))
    )
  )
}
#App server
app_server <- function(input, output, session) {
  # List the first level callModules here


  metafile <- callModule(mod_dataInput_server, "dataInput_ui_meta")

  output$metacontents <- renderTable({
    metafile()

  })

  genefile <- callModule(mod_dataInput_server, "dataInput_ui_gene")

  output$genecontents <- renderTable({
    genefile()

  })

}

Upvotes: 1

Views: 838

Answers (2)

ASH
ASH

Reputation: 20302

This is how I would do it.

library(shiny)
library(ggplot2)
#ui.R
ui <- fluidPage(
  titlePanel("My shiny app"), sidebarLayout(
sidebarPanel(
  helpText("This app shows how a user can upload a csv file. Then, plot the data.
          Any file can be uploaded but analysis is only available
          if the data is in same format as the sample file, downloadable below
          "),
  a("Data to be plotted", href="https://www.dropbox.com/s/t3q2eayogbe0bgl/shiny_data.csv?dl=0"),
  tags$hr(),
  fileInput("file","Upload the file"), 
  h5(helpText("Select the read.table parameters below")),
  checkboxInput(inputId = 'header', label = 'Header', value = TRUE),
  checkboxInput(inputId = "stringAsFactors", "stringAsFactors", FALSE),
  br(),
  radioButtons(inputId = 'sep', label = 'Separator', choices = c(Comma=',',Semicolon=';',Tab='\t', Space=''), selected = ',')
),
mainPanel(
  uiOutput("tb"),
  plotOutput("line")             
)
)
)

#server.R
server <- function(input,output){
data <- reactive({


file1 <- input$file
if(is.null(file1)){return()} 

read.table(file=file1$datapath, sep=input$sep, header = input$header, stringsAsFactors = input$stringAsFactors)})

output$filedf <- renderTable({
if(is.null(data())){return ()}
input$file
}) 

output$sum <- renderTable({
if(is.null(data())){return ()}
summary(data())
})

output$table <- renderTable({
if(is.null(data())){return ()}
data()
})

output$line <- renderPlot({
if (is.null(data())) { return() }
print(ggplot(data(), aes(x=date, y=aa)) + geom_line()+ facet_wrap(~station)) })

output$tb <- renderUI({if(is.null(data()))
h5()               
else
  tabsetPanel(tabPanel("About file", tableOutput("filedf")),tabPanel("Data", tableOutput("table")),tabPanel("Summary", tableOutput("sum")))
})
}


shinyApp(ui = ui, server = server)

enter image description here

enter image description here

Upvotes: 0

Colin FAY
Colin FAY

Reputation: 5109

As far as I see it, you've gotten the module correctly: you're reusing the UI and server on two different IDs for the data import.

You can optimize what you've done by creating a module for the table part, so writing:

# mod_table.R 

mod_table_ui <- function(id, name){
  ns <- NS(id)
  tabPanel(
    name,
    tableOutput(ns("metacontents"))
  )
}

# Module Server

#' @rdname mod_table
#' @export
#' @keywords internal

mod_table_server <- function(input, output, session, file){
  ns <- session$ns
  output$metacontents <- renderTable({
    file()
  })
}

And then in app_ui:

#' @import shiny
app_ui <- function() {
  tagList(
    # Leave this function for adding external resources
    golem_add_external_resources(),
    # List the first level UI elements here 
    navbarPage(
      "Tinsel",
      tabPanel(
        "Load Data",
        sidebarPanel(
          mod_dataInput_ui(
            "dataInput_ui_meta", 
            tags$div(
              "User META data", 
              tags$br(), 
              "(.csv, .tsv, or .txt file format)"
            )
          ), 
          helpText("Can add help text here"),
          # Horizontal line ----
          tags$hr(style="border-color: black;"),
          mod_dataInput_ui(
            "dataInput_ui_gene", 
            tags$div(
              "User GENETIC data", 
              tags$br(), 
              "(.csv, .tsv, or .txt file format)"
            )
          ),
          tags$hr(style="border-color: black;")
        ),
        mainPanel(
          tabsetPanel(
            mod_table_ui("table_ui_1", "Meta Data"),
            mod_table_ui("table_ui_2", "Genetic Data")
          )
        )
      )
    )
  )
}

And app_server:

app_server <- function(input, output, session) {
  # List the first level callModules here

  metafile <- callModule(mod_dataInput_server, "dataInput_ui_meta")

  callModule(mod_table_server, "table_ui_1", metafile)

  genefile <- callModule(mod_dataInput_server, "dataInput_ui_gene")

  callModule(mod_table_server, "table_ui_2", genefile)

}

Let me know if that answers your question.

Upvotes: 2

Related Questions