Search code examples
rshinyshiny-servershiny-reactivity

Shiny report : Trying to pass an input value as a parameter to an knit an .Rmd document but failing


This simple Shiny app has a dropdown of various selectable options (selectInput), each of the options in selectInput have a .Rmd file matching its name. Based on the option selected through the dropdown I'm trying to call its respective .Rmd file, complete code below.

The eventReactive() method seems to get the selected value and is passed as an input to call the respective .Rmd file, however the code executes but does not generate the report.

Alternatively, commenting out eventReactive() and calling out the required .Rmd file as a variable (report_name) generates a downloadable report with the required content. How do I go about fixing this to get the desired functionality?

app.R

library(shiny)

ui <- fluidPage(
  selectInput("select_id",
              "Select an option",
              list(
                " "=" ",
                "apples"='apples',
                "oranges"='oranges')
  ),
  radioButtons('format', 'Document format', c('HTML', 'Word'),
               inline = TRUE),
  
  downloadButton('downloadReport')
)

server <- function(input, output, session) {
  
  # download of report selectable format based on button press
  output$downloadReport <- downloadHandler(
    filename = function() {
      paste('my-report', sep = '.', switch(
        input$format, PDF = 'pdf', HTML = 'html'
      ))
    },
    
    # get selected option from dropdown and use the input value as the name to call the Rmd file
    report_name <- eventReactive(input$select_id, {
      paste(input$select_id,'Rmd', sep =".")
    }),

    # replacing report_name() with report_name [below line] generates report, but is not the desired outcome
    # report_name <- "apples.Rmd",
    
    content = function(file) { 
      cat('The selected fruit report is - - - :',report_name())
      
      src <- normalizePath(report_name())
      # temporarily switch to the temp dir, in case you do not have write
      # permission to the current working directory
      owd <- setwd(tempdir())
      on.exit(setwd(owd))
      file.copy(src, report_name(), overwrite = TRUE)
      
      out <- rmarkdown::render(report_name(),
                               switch(input$format,
                                      PDF = pdf_document(),
                                      HTML = html_document()
                               ))
      file.rename(out, file)
    } 
  ) 
}

shinyApp(ui, server)

apples.Rmd

---
title: "This is a report for Apples"
output: html_document
params:
  tbl: NA
  include: NA
---

oranges.Rmd

---
title: "This is a report for Oranges"
output: html_document
params:
  tbl: NA
  include: NA
---

Solution

  • A few things to correct:

    • report_name <- ... should be outside of the downloadHandler.
    • You should likely not allow a download if input$select_id is the " " choice. For this, we require it to be something else.
    • (Minor) You ::-reference render but not pdf_document, etc.
    server <- function(input, output, session) {
      # get selected option from dropdown and use the input value as the name to call the Rmd file
      report_name <- eventReactive(input$select_id, {                       # MOVED
        req(input$select_id != " ")                                         # ADDED
        paste(input$select_id,'Rmd', sep =".")
      })
    
      # download of report selectable format based on button press
      output$downloadReport <- downloadHandler(
        filename = function() {
          paste('my-report', sep = '.', switch(
            input$format, PDF = 'pdf', HTML = 'html'
          ))
        },
        content = function(file) {
          req(nzchar(report_name()))                                        # ADDED
          cat('The selected fruit report is - - - :',report_name())
    
          src <- normalizePath(report_name())
          # temporarily switch to the temp dir, in case you do not have write
          # permission to the current working directory
          owd <- setwd(tempdir())
          on.exit(setwd(owd))
          file.copy(src, report_name(), overwrite = TRUE)
    
          out <- rmarkdown::render(report_name(),
                                   switch(input$format,
                                          PDF = rmarkdown::pdf_document(),  # CHANGED
                                          HTML = rmarkdown::html_document() # CHANGED
                                   ))
          file.rename(out, file)
        }
      )
    }
    

    Follow-on notes:

    • You might want to test for presence of the report name before trying to render it, to preempt path-related problems where the user may not see the relevant error message. (Or not, depending on how confident you are that paths are being handled correctly.)

    • Your templates include params: but you are not passing anything in render(.). I'm assuming it was omitted here for brevity, but make sure you add params=... into the render call.

    • Your input$select_id offers "HTML" and "Word", yet your switch statements look for PDF and HTML. The above works when html is selected, but you need to update your selectInput and switch statements to ensure all candidates are referenced correctly.