Search code examples
rcsvshinyaction-button

r shiny: How to print a message in the app after the user forgets to upload a file?


I am building a rudimentary shiny app. First, I created a data frame 'x' and saved it in my working directory:

x <- data.frame(a = 1:4, b = 2:5)
write.csv(x, 'x.csv', row.names = F)

In my shiny I'd like to:

  1. Upload file 'x.csv'

  2. Click my action button 'Click Here' and run a few commands upon clicking it.

  3. Get a message printed in the Shiny app itself: "Load a file!" if I click on my button "Click here" after forgetting to upload the file first.

My code works, but I can't figure out how to make my message appear.

My code:

library(shiny)

ui <- fluidPage(
  br(),
  # User should upload file x here:
  fileInput("file_x", label = h5("Upload file 'x'!")),
  br(),
  # Users clicks the button:
  actionButton("do_it", "Click Here"),
  br(),
  # Print last value of the button 'do_it':
  verbatimTextOutput("print_action")
)


server <- function(input, output, session) {

  observeEvent(input$do_it, {

    # Just a check of my button's actions:
    output$print_action <- renderPrint({input$do_it})

    # Validating the input - next 5 lines are not working:
    # validate(
    #   need(
    #     try(is.null(input$file_x), "Load a file!")
    #     )
    # )

    # Reading in the file:
    fileData <- reactive({
      infile <- input$file_x
      if (is.null(infile)) {
        return(NULL)
      }
      read.csv(infile$datapath)
    })
    x <- fileData()

    # Writing out the same file - but under a different name:
    filename <- paste0("x", input$do_it, ".csv")
    write.csv(x, file = filename, row.names = FALSE)

  })
}

shinyApp(ui, server)

Solution

  • I think rather than displaying text, maybe modalDialog is better suited for what you are trying to achieve. I have implemented both solutions below, so you can compare.

    Note that I also modified the reading of the csv slightly. It is bad practice to set a reactive from inside an observer. In those cases, it is better to use a reactiveVal, and update that from an observer.

    Hope this helps!

    library(shiny)
    
    ui <- fluidPage(
      br(),
      # User should upload file x here:
      fileInput("file_x", label = h5("Upload file 'x'!")),
      br(),
      # Users clicks the button:
      actionButton("do_it", "Click Here"),
      br(),
      br(),
      # Print last value of the button 'do_it':
      verbatimTextOutput("print_action")
    )
    
    server <- function(input, output, session) {
    
      observeEvent(input$do_it, {
    
        if(is.null(input$file_x))
        {
          # show pop-up ...
          showModal(modalDialog(
            title = "Oh no!",
            paste0("You have not uploaded a file, silly person!"),
            easyClose = TRUE,
            footer = NULL
          ))
          # ... or update the text
          my_text('Please upload a file.')
        }
        else
        {
          # Reading in the file:
          infile <- input$file_x
          if (is.null(infile)) {
            return(NULL)
          }
          x <- read.csv(infile$datapath)
          fileData(x) # set the reactiveVal called fileData to the file inputs.
    
          # Writing out the same file - but under a different name:
          filename <- paste0("x", input$do_it, ".csv")
          write.csv(x, file = filename, row.names = FALSE)
          my_text('Succes!')
        }
      })
    
      fileData <- reactiveVal()
      my_text <- reactiveVal('')
      output$print_action <- renderText({my_text()})
    }
    
    shinyApp(ui, server)