Search code examples
rshinywidgetshiny-reactivity

RShiny - downloadButton Module not updating with new data


I'm having issues with a download button module downloading the correct dataset. Using it once it downloads as you'd expect, but once widget values change, and the download button is re-clicked, it downloads the previously selected variables, almost as if it's caching the values and not updating them. Same thing with the plot/filename titles.
Everything is wrapped in reactive as necessary, and the plot updates as the values change. Let me know if I'm doing something wrong. Cheers!

Note: issue persists even without usage of modules.

Reprex:


# Load libraries
library(shiny)
library(shinyWidgets)
library(tidyverse)
library(here)


# Load sources
source(here("dwnld buttons.R"))


# Load data
data(mtcars)

ui <- fluidPage(
  sidebarLayout(
    sidebarPanel(
      varSelectInput(inputId = "x_var",
                     data = mtcars,
                     label = "Select x var"),
      varSelectInput(inputId = "y_var",
                     data = mtcars,
                     selected = names(mtcars)[2],
                     label = "Select y var")
    ),
    mainPanel(
      plotOutput(outputId = "plot"),
      dwnldButtonUI(id = "download")
    )
  )
)

server <- function(input, output){
  
  
  plot1_data <- reactive({
    mtcars %>% 
      select(input$x_var, input$y_var) %>% 
      rename("x" = input$x_var,
             "y" = input$y_var)
  })
  
  labels <- reactiveValues()
  observeEvent(c(input$x_var, input$y_var), {
    labels$title1 <- paste(input$y_var, "vs", input$x_var)
  })
  
  
  output$plot <- renderPlot({
    
    ggplot(data = plot1_data(),
           aes(x = x,
               y = y))+
      geom_point()+
      labs(title = labels$title1)
    
  }
    
  )
  
  dwnldButtonServer(id = "download",
                    file_name = labels$title1,
                    graph_df = plot1_data()
  )
}

shinyApp(ui, server)

### Module ###

dwnldButtonUI <- function(id){
  ns <- NS(id)
  tagList(
    downloadButton(outputId = ns("dwnldBtn"),
                   label = "")
  )
}

dwnldButtonServer <- function(id, file_name, graph_df){
  
  moduleServer(
    id,
    function(input, output, session){
      
      output$dwnldBtn <- downloadHandler(
        
        filename = function() {
          paste(file_name, ".csv", sep = "")
        },
        
        content = function(file) {
          write.csv(graph_df, file, row.names = FALSE)
        }
      )
    }
  )
  
  
}

Solution

  • Utilising @Limey's comment, the solution can be extended to include to update both the download data, and filename, after widget values have been changed. This involves passing the reactive (not its value) to the module, and then accessing its value (not the reactive itself). See below:

    # Here we try and recreate a simplified version of the download button error #
    
    # Load libraries
    library(shiny)
    library(shinyWidgets)
    library(tidyverse)
    # library(here)
    
    
    # Load sources
    # source(here("dwnld buttons.R"))
    
    
    # Load data
    data(mtcars)
    
    ui <- fluidPage(
      sidebarLayout(
        sidebarPanel(
          varSelectInput(inputId = "x_var",
                         data = mtcars,
                         label = "Select x var"),
          varSelectInput(inputId = "y_var",
                         data = mtcars,
                         selected = names(mtcars)[2],
                         label = "Select y var")
        ),
        mainPanel(
          plotOutput(outputId = "plot"),
          dwnldButtonUI(id = "download")
        )
      )
    )
    
    server <- function(input, output){
      
      
      plot1_data <- reactive({
        mtcars %>% 
          select(input$x_var, input$y_var) %>% 
          rename("x" = input$x_var,
                 "y" = input$y_var)
      })
      
      labels <- reactiveValues()
      observeEvent(c(input$x_var, input$y_var), {
        labels$title1 <- paste(input$y_var, "vs", input$x_var)
      })
      
      
      output$plot <- renderPlot({
        
        ggplot(data = plot1_data(),
               aes(x = x,
                   y = y))+
          geom_point()+
          labs(title = labels$title1
               )
        
      })
      
      labels_title1 <- reactive({labels$title1})
    
      
      dwnldButtonServer(id = "download",
                        # File_name and graph_df is passed the reactive, not value
                        file_name = labels_title1,
                        graph_df = plot1_data
      )
    }
    
    shinyApp(ui, server)
    
    
    
    # Module UI
    dwnldButtonUI <- function(id){
      ns <- NS(id)
      tagList(
        downloadButton(outputId = ns("dwnldBtn"),
                       label = "")
      )
    }
    
    # Module server
    dwnldButtonServer <- function(id, file_name, graph_df){
      
      moduleServer(
        id,
        function(input, output, session){
          
          output$dwnldBtn <- downloadHandler(
            
            filename = function() {
              # Access the reactive value using ()
              paste(file_name(), ".csv", sep = "")
            },
            
            content = function(file) {
              # Access the reactive value using ()
              write.csv(graph_df(), file, row.names = FALSE)
            }
          )
        }
      )
      
    }