Once the download button has been clicked in a shiny app, a new page opens initialising the download. However, my download handler takes some time to produce the downloadable file which is shown in a progress bar on the main shiny page. Is there a way to keep the user on the main page or to prevent the download page from opening or postponing the download page till the file has been produced?
Many Thanks
Marcus
Vincent's solution using two buttons, an action button for the calculation and a download button for the download is the one I went with. An additional bonus to this solution is the progress bar that also comes in the shinyIncubator package.
An explanation of my code incase someone else wants to do the same thing:
The ui.R has an action button and a dynamic download button:
actionButton("makePlots", "Calculate Results"),
uiOutput("download_button")
and a progress initialisation for the progress bar:
mainPanel(
progressInit(),
uiOutput("mytabs")) # dynamic rendering of the tabs
The server.R is a little more complex. So that the download button is only shown when there is something to download I used the dynamic uiOutput with the following code:
output$download_button <- renderUI({
if(download){
downloadButton("downloadPlots", "Download Results")
}
})
The download button is only shown when download==TRUE
. At the start of server.R the variable is initialised: download<-FALSE
As the action button increases by 1 everytime it is clicked I included a counter (initial value 0) that increases after each "use" of the action button. Reason for this is the first if statement.
makePlots<-reactive({
if(input$makePlots>counter){ # tests whether action button has been clicked
dir.create("new_directory_for_output")
withProgress(session, min=1, max=15, expr={ # setup progress bar
for(i in 1:15){
png(paste0("new_directory_for_output/plot",i,".png"))
plot(i)
dev.off()
setProgress(message = 'Calculation in progress',
detail = 'This may take a while...',
value=i)
} # end for
}) # end progress bar
counter<<-counter+1 # needs the <<- otherwise the value of counter
# is only changed within the function
download<<-TRUE # something to download
} # end if
}) # end function
At this stage the function makePlots() doesn't have an output and isn't called anywhere so it does nothing. I therefore placed makePlots() at the beginning of each tab so that no matter which tab the user is on, once the action button has been clicked the plots are made and saved.
The final piece of teh puzzle is the download handler:
output$downloadPlots <- downloadHandler(
filename = function() { my_filename.zip },
content = function(file){
fname <- paste(file,"zip",sep=".")
zip(fname,new_directory_for_output) # zip all files in the directory
file.rename(fname,file)
unlink(new_directory_for_output,recursive = TRUE) # delete temp directory
download<<-FALSE # hide download button
}
) # end download handler
Here is an example of the html code generated for a downloadHandler
output :
<a id="downloadData" class="btn shiny-download-link shiny-bound-output" target="_blank" href="session/d832cc1f9218bd9e356572b089628030/download/downloadData?w=">Download</a>
The target attribute specifies where to open the href, target="_blank"
opens it in a new tab or new window.
By default (on many browser) when you open a new tab it will focus it, this is what you want to avoid, the problem is that you can't change the client default behaviour with some HTML/JS.
Moreover, change to target="self"
will open the href url in the same frame as it was clicked but the problem is that it will close the current session and you need this session openned (a tab with a localhost:port
url) to download the datas.
Though, you can add a non-disturbing note that the user could use Ctrl+Click to open the download without focusing the new empty tab.
For example :
helpText("Note : Use Ctrl+Click to open the download in background")
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With