shiny module for downloading plot

36 Views Asked by At

I am trying to develop a shiny module that is essentially a download plot button. Idea is to reuse this module in several places of my app. I am struggling though to get it working.

You can see a preview of the plot in this minimal app. When I click the download button I can see that the filename is not being rendered correctly:

enter image description here

and also it downloads an htm file and says "Couldn't download, no file"

library(shiny)
library(bs4Dash)
library(ggplot2)

#' download plot UI
#' @export
download_plot_UI <- function(id) {
  ns <- NS(id)
  tagList(
    bs4Dash::column(
      12,
      fluidRow(
        bs4Dash::column(
          width = 4,
          downloadButton(
            ns('download_plot'),'Download .png',
            width = '100%')
        ),
        bs4Dash::column(8)
      ),
      fluidRow(
        bs4Dash::column(
          width = 1,
          numericInput(
            ns('download_plot_width'), 'Width (in)',
            value = 7, step = 1, min = 1, max = 25),
        ),
        bs4Dash::column(
          width = 1,
          numericInput(
            ns('download_plot_height'), 'Height',
            value = 7, step = 1, min = 1, max = 25, width = '100%')
        ),
        bs4Dash::column(width = 9)
      )
    )
  )
}

#' download plot server
#' @export
#' @param gg ggplot2 object
#' @param prefix string prefix to add to filename
download_plot_server <- function(id, gg, prefix = '') {
  stopifnot(is.reactive(gg))
  moduleServer(
    id,
    function(input, output, session) {

      output$download_plot <- downloadHandler(
        filename = function() {
          stringr::str_glue(
            "{Sys.Date()}_",
            "w{input$download_plot_width}",
            "h{input$download_plot_height}",
            "scdotplot.png")
        },
        content = function(file) {
          filename <- paste0(prefix, '_', file)
          message(glue::glue(
            'Printing dotplot: {filename} ',
            'Width: {input$download_plot_width} ',
            'Height: {input$download_plot_height}'
          ))
          ggsave(
            plot = gg(),
            file = filename,
            width = input$download_plot_width,
            height = input$download_plot_height
          )
        }
      )
    }
  )
}

#' @export
#' @examples \dontrun{
#'  download_plot_app()
#' }
download_plot_app <- function(...) {

  # ui----
  ui <- fluidPage(
    titlePanel("download_plot module"),
    shiny::plotOutput('ggplot'),
    download_plot_UI('download_plot')
  )

  server <- function(input, output, session) {

    p <- reactive(
      ggplot(mtcars, aes(mpg, cyl)) + geom_point()
    )
    output$ggplot <- renderPlot(p())
    download_plot_server(
      'download_plot',
      gg =  p
    )
  }
  shinyApp(ui, server, ...)
}


download_plot_app()
1

There are 1 best solutions below

0
Victor Yuan On

from @stefan:

The issue is that you add the prefix in the content = function. Instead move it to filename = , i.e. do

filename = function() { stringr::str_glue("{prefix}_{Sys.Date()}_", ....)