shinyshinyjs

How can I create a table with download buttons in shiny?


I have a shiny app and in this app a table that gives an overview over some items. I want to make it possible to the user to download these items by clicking on a button.

My current approach works somehow, but leads to a very stange behaviour if I modify seemingly unrelated parts.

Here is an explanation of my minimal example:

I have a list of two data.frames for which I want to offer download options. To achieve this, I make action buttons, that write the wanted data.frame to an input using an onclick event. When this textinput is changed, it triggers a shiny download button to download the wanted data.frame.

This approach seems pretty complicated, but usually it works great. It seems that the download-Button is complicating things.

library(shiny)
library(shinyjs)

ui <- fluidPage(
  DT::dataTableOutput("dt"),
  shinyjs::useShinyjs(),
  div( # replace 'div' by 'hidden' and see what happens
    "should not be visible to user",
    textInput(inputId = "datasetToDownload", label = "Dataset to Download"),
    downloadButton(outputId = "downloadButton")
  )
)
datasetsToDownload <- c("iris", "mtcars")
server <- function(input, output, session) {
  output$dt <- DT::renderDT({
    df <- data.frame(
      "datasets" = datasetsToDownload,
      "download" = sapply(datasetsToDownload, function(id) {
        as.character(actionButton(
          inputId = paste0("download", id),
          label = paste("download", id),
          onclick = sprintf("Shiny.setInputValue('datasetToDownload', '%s', {priority: 'event'})", id)
        ))
      })
    )
    DT::datatable(
      df,
      escape = setdiff(names(df), c("download"))
    )
  })
  observeEvent(input$datasetToDownload,{
    if (input$datasetToDownload %in% datasetsToDownload){
      shinyjs::click("downloadButton")
    }
  })
  output$downloadButton <- downloadHandler(
    filename = function() {
      # Use the selected dataset as the suggested file name
      filename <- paste0(input$datasetToDownload, ".csv")
      print("filename:")
      print(filename)
      filename
    },
    content = function(file) {
      print("content")
      print("file:")
      print(file)
      # Write the dataset to the `file` that will be downloaded
      write.csv(get(input$datasetToDownload), file)
    }
  )
}

shinyApp(ui, server, options = list(launch.browser = TRUE))

This works fine, but when I hide the textInput and downloadButton, it downloads a 49aqnopL.htm that is close to but not exactly equal to the html-code of the shiny app. Also the print statements inside the downloadHandler do not happen. I find this behaviour very surprising. Do you know why this happens?

Also: Do you have any suggestion, how I can achieve my goal?


Solution

  • This is basically a duplicate of R Shiny - How to have an action button that automatically downloads a csv file (Not download button?)

    Per that, switching to conditionalPanel("false") instead of hidden() solves the issue:

      conditionalPanel(
        "false",
        "should not be visible to user",
        textInput(inputId = "datasetToDownload", label = "Dataset to Download"),
        downloadButton(outputId = "downloadButton")
      )