rfileshinydownloadwrite.table

In a shiny app how can I let the user choose the filename and directory to download with write.table


This is a follow-up question to this

Now I somehow managed to download the reactive dataframe to my hard drive (!not server or working directory) and append each new entry as new line with write.table.

Interestingly write.csv does not work because it does not allow append argument https://stat.ethz.ch/pipermail/r-help/2016-August/441011.html.

With this minimal working app, I would like to know how I can get the user to choose a directory and a filname to download there. Now I have this absolut path: file = "C:/Users/yourname/Downloads/my_df.csv" which works. But I don't know if it will work for other user!

library(shiny)
library(shinyWidgets)

ui <- fluidPage(
  sidebarLayout(
    
    sidebarPanel(width = 4,
                 sliderInput("a", "A", min = 0, max = 3, value = 0, width = "250px"),
                 actionButton("submit", "Submit")
                 ),
    
    mainPanel(
      titlePanel("Sliders"),
      tableOutput("values")
    )
  )
)
server <- function(input, output, session) {
 
  sliderValues <- reactive({
    data.frame(Name = c("A"), Value = as.character(c(input$a)), stringsAsFactors = FALSE)
  })
  
  output$values <- renderTable({
    sliderValues()
  }) 
  
  # Save the values to a CSV file on the hard disk ----
  saveData <- reactive({write.table(sliderValues(), file = "C:/Users/yourname/Downloads/my_df.csv", col.names=!file.exists("C:/Users/yourname/Downloads/my_df.csv"), append = TRUE) })
  
  observeEvent(input$submit, {
    saveData()
  })
}
shinyApp(ui, server)

The requirement is that the user should see a modal dialog ui with the question "In which folder with which filename you want to download?". Quasi like the things we do daily if we download from the internet.


Solution

  • I now solved it this way:

    I realized that I have two options:

    1. As suggested by @Stéphane Laurent using downloadhandler
    2. Using DT::datatable()

    I have decided to use number 2. Many thanks to all of your inputs!

    library(shiny)
    library(shinyWidgets)
    library(DT)
    
    ui <- fluidPage(
      sliderInput("a", "A", min = 0, max = 3, value = 0, width = "250px"),
      titlePanel("Sliders"),
      dataTableOutput("sliderValues", width="450px")
    )
    server <- function(input, output, session) {
      
      sliderValues <- reactive({
        df <- data.frame(
          Name = "A", 
          Value = as.character(c(input$a)), 
          stringsAsFactors = FALSE) %>% 
          pivot_wider(names_from = Name, values_from = Value)
        
        return(df)
      })
    
      
      # Show the values in an HTML table in a wider format----
      output$sliderValues <- DT::renderDataTable({
        DT::datatable(sliderValues(),
                      extensions = c('Buttons'),
                      options = list(
                        dom = 'frtBip',
                        buttons = list(list(extend = 'excel', filename = paste0("myname-", Sys.Date())))
                      )
        )
      })
    
    }
    shinyApp(ui, server)
    

    enter image description here