rshiny

Catching errors within an R Shiny downloadHandler


When an error occurs within a downloadHandler object, the app will still pop up a download window. Generally the only indication that an error has occurred is that the default filename and type are not right in the pop-up. In this example:

ui <- fluidPage(
  downloadLink("downloadData", "Download")
)

server <- function(input, output) {
  data <- mtcars
  
  output$downloadData <- downloadHandler(
    filename = function() {
      paste("data-", Sys.Date(), ".csv", sep="")
    },
    content = function(file) {
      stop()
      write.csv(data, file)
    }
  )
}

shinyApp(ui, server)

The stop() here simulates an error. When running the script, the only indication that the widget did not work correctly is that the default filename is downloadData.htm instead of data-[date]-.csv.

Is there a way to catch this error and prevent the download window from launching?


Solution

  • This is a modification of @Eliot Dixon's answer that removes the requirement to know in advance if the downloadHandler logic will fail. The key is to have an always-hidden downloadButton and an always-visible actionButton. When the actionButton is clicked, run the complicated logic. If it succeeds, save the resulting data to a reactiveVal and then use shinyjs::click() to simulate clicking on the real, but hidden, downloadButton.

    library(shiny)
    library(shinyjs)
    
    ui <- fluidPage(
        useShinyjs(),
        selectInput("dataExists", "Data exists", choice = c("TRUE", "FALSE")),
        conditionalPanel("false", downloadButton("downloadData", "Download")),
        actionButton("click", "Download")
    )
    
    server <- function(input, output) {
        computed_data <- reactiveVal()
        
        observeEvent(input$click, {
            # Put all content logic here.  
            # If it fails, showNotification.
            # If it succeeds, store result and click button.
            if(input$dataExists == "FALSE"){
                showNotification("Data error.")
            } else {
                computed_data(mtcars)
                click("downloadData")
            }
        })
        
        output$downloadData <- downloadHandler(
            filename = function() {
                paste("data-", Sys.Date(), ".csv", sep="")
            },
            content = function(file) {
                write.csv(computed_data(), file)
            }
        )
    }
    
    shinyApp(ui, server)