Search code examples
rshinyr-markdownshinyjs

How to disable a downloadButton in shiny when rmarkdown is rendering a report?


How can I disable a downloadButton while server is busy rendering a Rmarkdown report?

The ideal would be to make the button disable only while the server is busy rendering the report, if the server is not busy the downloadButton would enable again.

In the reprex below I tried to use shinyjs::onclick() function to disable the button when it's pressed, but it's only disable when the first Rmarkdown is downloaded by the user. Which is a problem because user can click multiple times until the button gets disabled.

Recipe to the problem:

  1. Put the two files (app.R and report.Rmd) in same folder
  2. Run the shinyApp()
  3. Double click in "Generate report" button

app.R

library(shiny)
library(shinyjs)

report_path <- tempfile(fileext = ".Rmd")
file.copy("report.Rmd", report_path, overwrite = TRUE)

render_report <- function(input, output, params) {
    rmarkdown::render(
        input,
        output_file = output,
        params = params,
        envir = new.env(parent = globalenv())
    )
}

ui <- fluidPage(
    useShinyjs(), # required to use shinyjs
    sliderInput("n", "Number of points", 1, 100, 50),
    downloadButton("report", "Generate report")
)

server <- function(input, output) {
    output$report <- downloadHandler(
        filename = "report.html",
        content = function(file) {
            params <- list(n = input$n)
            callr::r(render_report,
                     list(
                         input = report_path,
                         output = file,
                         params = params
                     ))
        }
    )
    
    onclick("report", disable("report")) # disable button when it's pressed
    
}

shinyApp(ui, server)

report.Rmd

---
title: "Dynamic report"
output: html_document
params:
  n: NA
---

A plot of `r params$n` random points.

```{r}
plot(rnorm(params$n), rnorm(params$n))
```

My question is based on the following example:

Chapter's link: Mastering Shiny Book

Github's link: Github


Solution

  • Do following:

    library(shiny)
    library(shinyjs)
    
    report_path <- tempfile(fileext = ".Rmd")
    file.copy("report.Rmd", report_path, overwrite = TRUE)
    
    render_report <- function(input, output, params) {
        rmarkdown::render(
            input,
            output_file = output,
            params = params,
            envir = new.env(parent = globalenv())
        )
    }
    
    ui <- fluidPage(
        useShinyjs(), # required to use shinyjs
        sliderInput("n", "Number of points", 1, 100, 50),
        downloadButton("report", "Generate report")
    )
    
    server <- function(input, output) {
        output$report <- downloadHandler(
            filename = "report.html",
            content = function(file) {
                disable("report")
                on.exit(enable("report"))
                params <- list(n = input$n)
                callr::r(render_report,
                         list(
                             input = report_path,
                             output = file,
                             params = params
                         ))
            }
        )
    }
    
    shinyApp(ui, server)
    

    First disable the button when content generation started, and then use on.exit to reenable the button. on.exit makes sure when the function quits, the button is guaranteed to be enabled again, even if an error happens.

    enter image description here