Download button auto clear by any input change

I have two input selection and an action button to generate a plot and download the data. I would like to clear the output contents (plot and download button) any time there is a change in the input selection. The code below will only clear the plot and not the download button. Not sure if the reactiveValues under the downloadhandler is correct.

library(shiny)
library(ggplot2)
library(openxlsx)

ui = fluidPage(
  textInput("textT", label = "Title", value = ""),
  textInput("textX", label = "X-Axis Label", value = ""),
  actionButton("Btn", "Run", icon=icon("play-circle")),
  plotOutput('plot1'),
  conditionalPanel(condition = "input.Btn>0", downloadButton("dwload", "Download"))
  )

server = function(input, output, session) {    

  v <- reactiveValues(clearAll = TRUE)

  observeEvent(c(input$textT, input$textX), {
    v$clearAll <- TRUE
  }, priority = 10)

  observeEvent(input$Btn, {

    output$plot1 = renderPlot({
      if (v$clearAll) 
        return()
      else
        ggplot(mtcars, aes(x= gear, y= carb)) + geom_line() +ggtitle(input$textT) + xlab(input$textX)
    })

    output$dwload <- downloadHandler(
        filename = function() {
          paste0("Checks-", gsub(" ", "_", gsub(":", ".", Sys.time())), ".xlsx")
        },
      content = function(file) {
        if (v$clearAll) 
          return()
        else
          quick_xlsx(mtcars, file=file)
      }
    )

    v$clearAll <- FALSE

  }, priority = 10)

}

shinyApp(ui, server)

I'd appreciate any help.

Thank you!

The first issue is that conceptually the logic test for if to show the download button relied on button press being > 0 which it will always continue to be, so would never go away.
Since you already track a v$clearAll as a condition, it made sense to rely on that as a rule instead BUT conditional panel works via javascript, and it thus is a non-trivial issue to get it to know the status of shiny reactivevalues, therefore I simply replaced the conditionalpanel with a 'purer' shiny approach, for which access reactive values is trivial.

It seems to me that a refactor could be considered also, seperating out the plot logic, to a reactive, this could then be simply rendered, but also it could be req(uired) to dynamically show the download button, which would very slightly simplify that aspect

Finally, I observed that pressing run without any title or xaxis values, was possible, so I wanted to show that it would be easy to protect against that with a few req() calls.

Here is working R code for you

library(shiny)
library(ggplot2)
library(openxlsx)

ui = fluidPage(
  textInput("textT", label = "Title", value = ""),
  textInput("textX", label = "X-Axis Label", value = ""),
  actionButton("Btn", "Run", icon=icon("play-circle")),
  plotOutput('plot1'),
  textOutput("btnout"),
  textOutput("vclearAll"),
  uiOutput("mydownloadui")
)

server = function(input, output, session) {    
  
  v <- reactiveValues(clearAll = TRUE)
  
  output$btnout <- renderPrint({
    paste0("input$Btn : ",input$Btn)
  })
  output$vclearAll <- renderPrint({
    paste0("v$clearAll : ",v$clearAll)
    
  })
  observeEvent(c(input$textT, input$textX), {
    v$clearAll <- TRUE
  }, priority = 10)
  
  observeEvent(input$Btn, {
    
    output$plot1 = renderPlot({
      req(input$textT)
      req(input$textX)
      if (v$clearAll) 
        return()
      else
        ggplot(mtcars, aes(x= gear, y= carb)) + geom_line() +ggtitle(input$textT) + xlab(input$textX)
    })
    
    output$mydownloadui <- renderUI({
      req(input$textT)
      req(input$textX)
      
      if(v$clearAll)
        return(NULL)
      downloadButton("dwload", "Download")
    })
    
    output$dwload <- downloadHandler(
      filename = function() {
        paste0("Checks-", gsub(" ", "_", gsub(":", ".", Sys.time())), ".xlsx")
      },
      content = function(file) {
        if (v$clearAll) 
          return()
        else
          quick_xlsx(mtcars, file=file)
      }
    )
    
    v$clearAll <- FALSE
    
  }, priority = 10)
  
}

shinyApp(ui, server)
1 Like

Many thanks @nirgrahamuk for the clarification. I indeed already accepted your response as solution.

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.

Thank you very much @nirgrahamuk. This solved the trick. Just a small hint. Can we have the two req() only once?

observeEvent(input$Btn, {
    
    req(input$textT)
    req(input$textX)
 
    output$plot1 = renderPlot({
      if (v$clearAll) 
        return()
      else
        ggplot(mtcars, aes(x= gear, y= carb)) + geom_line() +ggtitle(input$textT) + xlab(input$textX)
    })
    
    output$mydownloadui <- renderUI({
      if(v$clearAll)
        return(NULL)
      downloadButton("dwload", "Download")
    })
    output$dwload <- downloadHandler(
      filename = function() {
        paste0("Checks-", gsub(" ", "_", gsub(":", ".", Sys.time())), ".xlsx")
      },
      content = function(file) {
        if (v$clearAll) 
          return()
        else
          quick_xlsx(mtcars, file=file)
      }
    )
    v$clearAll <- FALSE
  }, priority = 10)

the req() are guard statements that say what is required for a function to continue. If you want two different elements to have the same guard statements, then there will be repetition, you could reduce the literal symbols, by hiding them away in some function, but I don't think its worthwhile.

Oh, another option, is to not have them explicitly on their own at the start of the function, but drop them in just when the variable itself is encountered, for example :

observeEvent(input$Btn, {
    
    output$plot1 = renderPlot({
      if (v$clearAll) 
        return()
      else
        ggplot(mtcars, aes(x= gear, y= carb)) + geom_line() +ggtitle(req(input$textT)) + xlab(req(input$textX))
    })