I have a shiny app where users enter data and then click an action button to submit that data into a database. I also have a download button that lets them download a copy of the data locally as a csv.
What I am trying to now achieve is have that data automatically download when the user clicks the action button. I have looked online and couldn't find much info this. Any help or thoughts is apprecaited.
I have a small app code below.
library(shiny)
library(gt)
#------------------------------#
# GLOBAL
#------------------------------#
gt_tbl <-
gtcars %>%
gt()
#------------------------------#
# UI
#------------------------------#
ui <- fluidPage(
downloadButton('downloadData', 'Download data'),
actionButton('button', "ACTOIN BUTTON"),
gt_output(outputId = "table")
)
#------------------------------#
# SERVER
#------------------------------#
server <- function(input,output,session) {
#ACTION BUTTON
observeEvent(input$button, {
withProgress(message = "You clicked this button.... Working...", value =0 , {
Sys.sleep(3)
#INSERT FUNCTION HERE LIKE WRITING TO DB
incProgress(.25,message = "Writing to database.... Working...")
Sys.sleep(3)
#AUTOMATICALLY DOWNLOAD BUTTON NEXT?
incProgress(.35, message = "Saving a copy of data locally.... Working...")
#output$downloadData
Sys.sleep(3)
incProgress(1, message = "COMPLETE!")
Sys.sleep(3)
})
})
# TABLE
output$table <- render_gt(
expr = gt_tbl
)
# TRADITIONAL DOWNLOAD BUTTON
output$downloadData <- downloadHandler(
filename = function() {
paste("dataset-gtcars-", Sys.Date(), ".csv", sep="")
},
content = function(file) {
write.csv(gtcars, file)
})
}
#------------------------------#
# RUN APP
#------------------------------#
shinyApp(ui=ui,server=server)
Combining the answer found here with your code, you can do this:
library(shiny)
library(gt)
library(shinyjs)
#------------------------------#
# GLOBAL
#------------------------------#
gt_tbl <-
gtcars %>%
gt()
# function which checks the data; returns TRUE or FALSE
checkData <- function(dat){
TRUE
}
# function which transforms the data; returns NULL if check not TRUE
processData <- function(dat){
if(checkData(dat)){
# do something with dat
names(dat) <- toupper(names(dat)) # for our example
return(dat)
}else{
return(NULL)
}
}
#------------------------------#
# UI
#------------------------------#
ui <- fluidPage(
useShinyjs(),
conditionalPanel(
"false", # always hide the download button
downloadButton("downloadData")
),
actionButton("button", "Download"),
gt_output(outputId = "table")
)
#------------------------------#
# SERVER
#------------------------------#
server <- function(input,output,session) {
finalData <- reactiveVal() # to store the processed data
#ACTION BUTTON
observeEvent(input$button, {
withProgress(message = "You clicked this button.... Working...", value =0 , {
Sys.sleep(3)
#INSERT FUNCTION HERE LIKE WRITING TO DB
incProgress(.25,message = "Writing to database.... Working...")
Sys.sleep(3)
# PROCESS DATA
if(!is.null(df <- processData(gtcars))){
finalData(df)
runjs("$('#downloadData')[0].click();") # DOWNLOAD BUTTON
}else{
# something which throws an alert message "invalid data"
# (eg with shinyBS::createAlert or shinyWidgets::sendSweetAlert)
}
Sys.sleep(3)
incProgress(1, message = "COMPLETE!")
Sys.sleep(3)
})
})
# TABLE
output$table <- render_gt(
expr = gt_tbl
)
# DOWNLOAD BUTTON
output$downloadData <- downloadHandler(
filename = function() {
paste("dataset-gtcars-", Sys.Date(), ".csv", sep="")
},
content = function(file) {
write.csv(finalData(), file)
})
}
#------------------------------#
# RUN APP
#------------------------------#
shinyApp(ui=ui,server=server)
The app includes an action button that simulates a process of writing data to a database and then downloading the processed data as a CSV file. The processData
function takes the input data and transforms it (in this case, it changes the column names to all uppercase; I kept that the same from the other example answer). The checkData
function always returns TRUE
, but you can customize it to check the validity of your data.
When the action button is clicked, a progress bar appears with a message indicating that the app is working. After a few seconds of delay, the data is processed, and the download button is automatically clicked (using JavaScript) to download the .csv file. If the data is invalid, it could throw an alert message using shinyBS::createAlert
, shinyWidgets::sendSweetAlert
functions etc (the code doesn't include this functionality, I just wrote in the same comment from the other answer).
The solution is mainly to hide the download button by default using a conditionalPanel
with "false"
as the condition. This ensures that the download button only appears after the user clicks the action button and the data is processed successfully.