1

The below reproducible code allows the user to select either a data table or a plot of the data for viewing (via input$view). I'm trying to create a conditional around the downloadHandler() so that if the user is viewing the data table and chooses to download, then the data is downloaded; otherwise if the user is viewing the plot and chooses to download then a plot in PNG format is downloaded. I'm running into issues around input$view reactivity. How would I modify the code below to conditionally download whichever (data or plot) the user is viewing?

The code as posted below works for viewing either data or plot, but only allows the data table to be downloaded. Offending lines of code that otherwise cause a crash are commented out.

Reproducible code:

library(shiny)
library(ggplot2)

ui <- fluidPage(
  radioButtons("view",
               label = "View data or plot",
               choiceNames = c('Data','Plot'),
               choiceValues = c('viewData','viewPlot'),
               selected = 'viewData',
               inline = TRUE
  ),
  conditionalPanel("input.view == 'viewData'",tableOutput("DF")),
  conditionalPanel("input.view == 'viewPlot'",plotOutput("plotDF")),
  downloadButton("download","Download",style = "width:20%;")
)

server <- function(input, output, session) {
  
  data <- data.frame(Period = c(1,2,3,4,5,6),Value = c(10,20,15,40,35,30))
  
  data1 <- reactiveValues()
  
  inputView <- reactive(input$view) # attempt to make input$view reactive
  
  observeEvent(input$view,{data1$plot <- ggplot(data, aes(Period,Value)) + geom_line()})
  
  output$DF <- renderTable(data)
  output$plotDF <- renderPlot(data1$plot)
  
  output$download <-
    # if(inputView() == 'viewData'){
      downloadHandler(
        filename = function() 
        paste("dataDownload","csv",sep="."),
        content = function(file){
          write.table( 
            data,
            na = "", 
            file, 
            sep = ",", 
            col.names = TRUE, 
            row.names = FALSE)
        }
      )
    # }
  
    # else{
    #   downloadHandler(
    #     filename = function(){paste("plotDownload",'.png',sep='')},
    #     content = function(file){
    #       ggsave(file,plot=data1$plot)
    #     }
    #   )
    # }
  
}

shinyApp(ui, server)

1 Answers1

1

Try this

library(shiny)
library(ggplot2)

ui <- fluidPage(
  radioButtons("view",
               label = "View data or plot",
               choiceNames = c('Data','Plot'),
               choiceValues = c('viewData','viewPlot'),
               selected = 'viewData',
               inline = TRUE
  ),
  conditionalPanel("input.view == 'viewData'",tableOutput("DF")),
  conditionalPanel("input.view == 'viewPlot'",plotOutput("plotDF")),
  #downloadButton("download","Download",style = "width:20%;")
  uiOutput("plotrtable")
)

server <- function(input, output, session) {
  
  data <- data.frame(Period = c(1,2,3,4,5,6),Value = c(10,20,15,40,35,30))
  
  data1 <- reactiveValues()
  
  inputView <- reactive(input$view) # attempt to make input$view reactive
  
  observeEvent(input$view,{data1$plot <- ggplot(data, aes(Period,Value)) + geom_line()})
  
  output$DF <- renderTable(data)
  output$plotDF <- renderPlot(data1$plot)
  
  output$plotrtable <- renderUI({
    if(input$view == 'viewData'){downloadButton("download","Download",style = "width:20%;") }
    else {downloadButton("downloadp","Download",style = "width:20%;") }
  })
  
  output$download <-  downloadHandler(
      filename = function() 
        paste("dataDownload","csv",sep="."),
      content = function(file){
        write.table( 
          data,
          na = "", 
          file, 
          sep = ",", 
          col.names = TRUE, 
          row.names = FALSE)
      }
  )
  
  output$downloadp <- downloadHandler(
      filename = function(){paste("plotDownload",'.png',sep='')},
      content = function(file){
        ggsave(file,plot=data1$plot)
      }
  )
  
}

shinyApp(ui, server)
YBS
  • 19,324
  • 2
  • 9
  • 27
  • In your solution I eliminated the reactive inputView, and in your output$plotrtable <- renderUI(...) I replaced inputView() with input$view, and it works fine. Simplifies the code slightly. I introduced the inputView() reactive when I was flailing around for a solution for the error I kept getting: "Error : Operation not allowed without an active reactive context. * You tried to do something that can only be done from inside a reactive consumer." – Curious Jorge - user9788072 May 22 '22 at 15:16
  • I would do the same. However, I kept your logic as you may have other reasons to keep it that way depending on your use case. – YBS May 22 '22 at 15:19