0

I have a simple example Siny code that works fine (below). It generates a list of several tiny data frames (reactive object 'myout' in server.R). The number of the data frames is based on user's input.('NumRuns'). My current code allows the user to download the first of these data frames - via downloadButton in ui.R and downloadHandler in server.r. I am wondering if it's possible to create a loop around both downloadButton and downloadHandler so that - after the analysis is run (actionButton) - the user gets as many buttons to download as there were data frames generated in 'myout'. Is it even possible?

Or maybe it's possible to create one downloadButton that allows the user to 'pick' which data frame s/he wants to download? Thanks a lot for any hints - because I am not sure how to approach it!

My code (I run it using: runApp(launch.browser = T)

### My ui.R code:

shinyUI(pageWithSidebar(

  headerPanel("My App"),

  sidebarPanel(
    numericInput('NumRuns','Number of runs',value=3,min=3,max=10,step=1),
    br(),
    actionButton(inputId="goButton","Run!"),
    br(),
    br(),
    textInput("downloadData","Save My Data Frame:",value="Data Frame 1"),
    downloadButton('downloadData','Save my file!')

  ),

  mainPanel(
    tabsetPanel(
      tabPanel("Shows the 1st data frame",tableOutput("mydf"))
    )
  )
))


### My 'server.R' code:
shinyServer(function(input,output){


  ### Creating files for displaying and downloading

  myout = reactive({
    if(input$goButton==0) return(NULL)

    nrruns=input$NumRuns
    mylist=NULL
    for(i in 1:nrruns){
      mylist[[i]]<-data.frame(a=rnorm(10),b=runif(10))
      names(mylist)[i]<-paste("dataframe",i,sep="")
    }
    return(mylist)
  })

  # Grabbing only the 1st data frame:
  output$mydf <- renderTable({
    if(input$goButton==0) return(NULL)
    myout()$dataframe1
  })

  # Allowing to download only the 1st data frame:
  output$downloadData <- downloadHandler(
    filename = function() { paste(input$downloadData, " ",Sys.Date(),".csv",sep="") },
    content = function(file) {
      write.csv(myout()$dataframe1,file,row.names=F)
    }
  )

})
Thomas
  • 43,637
  • 12
  • 109
  • 140
user3245256
  • 1,842
  • 4
  • 24
  • 51

1 Answers1

3

This post might gives you the answer.

https://groups.google.com/forum/#!msg/shiny-discuss/qGN3jeCbFRY/xOW5qoVrr94J

Yes, this is possible. You'll need to use uiOutput/renderUI to render the buttons, each with a different ID (e.g. downloadData1 through downloadDataN). For defining the download handlers dynamically, use a pattern like:

observe({
  lapply(1:N, function(i) {
    output[[paste0("downloadData", i)]] <- downloadHandler(...)
  })
})

the important thing there being that you can assign to output[[id]] if your output names are dynamic.

Xiushi Le
  • 245
  • 3
  • 16