Reputation: 2783
I would like a shiny app to offer the user the ability to download multiple versions of the same report, depending on user-defined parameters. Using the iris
dataset as an example, the idea would be that a report would be generated for each user-defined subset of the dataset (by Species
).
My code for the report (test.Rmd
) is below:
---
title: "Parametrised report"
output: pdf_document
params:
species:
label: Species
input: select
choices: [setosa, versicolor, virginica]
---
```{r}
df <- dplyr::filter(iris, Species %in% params$species)
library(ggplot2)
ggplot(df, aes(Sepal.Length, Petal.Length)) + geom_point()
```
My code for the app.R
file is below:
# Define the ui code
ui <- bootstrapPage(
selectInput('species', 'Select species:',
paste(unique(iris$Species)),
multiple = TRUE),
downloadButton('DownloadReport')
)
# Define the server code
server <- function(input, output) {
flower_species <- reactive({input$species})
observe({
lapply(flower_species(), function(i) {
output$DownloadReport <- downloadHandler(
filename = paste0('test_', i, '.pdf'),
content = function(file) {
out <- rmarkdown::render('test.Rmd', rmarkdown::pdf_document(), params = list(species = i))
file.rename(out, file)
}
)
})
})
}
# Return a Shiny app object
shinyApp(ui = ui, server = server)
The user would define the species (one or more of setosa, versicolor, virginica
), and if several were selected, not only would the report subset the data to include just that species per report (df <- dplyr::filter(iris, Species %in% params$species)
), but a new report would be generated for each of the selected species.
I found this link: Shiny: dynamic UI - a loop around downloadHandler? which builds on this discussion: https://groups.google.com/forum/#!msg/shiny-discuss/qGN3jeCbFRY/xOW5qoVrr94J, but this isn't quite what I need - I don't want multiple download buttons, but one download button that creates independent reports based on the inputs to selectInput
. I was hoping the lapply
approach shown above would work, but only one report is created. Additionally, I tried a looping approach, but that didn't work (error message was that I tried to do something reactive outside of a reactive environment).
I'm surprised not to find this anywhere - it seems like something that would be useful in many shiny apps. Any help is greatly appreciated.
Upvotes: 0
Views: 725
Reputation: 1567
I managed to get a working version but it does not work in the RStudio previewer, you have to click on the Show in browser
button.
I think you need to remove some parts of the YAML in test.Rmd
that are not needed here:
---
title: "Parametrised report"
output: pdf_document
params:
species: setosa
---
```{r}
df <- dplyr::filter(iris, Species %in% params$species)
library(ggplot2)
ggplot(df, aes(Sepal.Length, Petal.Length)) + geom_point()
```
app.R looks like this :
library(magrittr)
# Define the ui code
ui <- bootstrapPage(
selectInput('species', 'Select species:',
paste(unique(iris$Species)),
multiple = TRUE),
downloadButton('DownloadReport')
)
# Define the server code
server <- function(input, output) {
flower_species <- reactive({input$species})
output$DownloadReport <- downloadHandler(
filename = "test.zip",
content = function(file){
files <- lapply(flower_species(), function(i) {
rmarkdown::render('test.Rmd', rmarkdown::pdf_document(), output_file = paste0(i, ".pdf"), params = list(species = i))
}) %>% unlist %>% basename
zip(file, files)
file
}
)
}
# Return a Shiny app object
shinyApp(ui = ui, server = server)
It is basically the same principle that you wrote earlier but the lapply
loop is inside the downloadHandler content
element, and you use the zip
function to zip all the generated reports into a single zip file that is returned. Maybe throw an unlink
after the zip
in order to remove all the pdfs.
Upvotes: 2