A loop around downloadButton in ui.R and around downloadHandler in server.R: is it possible?

2,295 views
Skip to first unread message

Dimitri Liakhovitski

unread,
Sep 23, 2014, 9:52:38 AM9/23/14
to shiny-...@googlegroups.com
Hello!

I have a simple example 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)
    }
  )
  
})

Joe Cheng

unread,
Sep 24, 2014, 5:01:32 AM9/24/14
to Dimitri Liakhovitski, shiny-...@googlegroups.com
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.

--
You received this message because you are subscribed to the Google Groups "Shiny - Web Framework for R" group.
To unsubscribe from this group and stop receiving emails from it, send an email to shiny-discus...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/shiny-discuss/82ec4269-5936-4e7f-86d1-8f8bfd9f523d%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Dimitri Liakhovitski

unread,
Sep 24, 2014, 9:14:44 AM9/24/14
to shiny-...@googlegroups.com, dl7...@gmail.com
Thank you so much, Joe!

I really don't know much about HTML and don't understand how exactly use uiOutput/renderUI?
I found some examples online, but not sure how to apply them to my case.
Where should I put that observe statement? in "server.R"?

Thank you and sorry for being dumb.
D.

Joe Cheng

unread,
Sep 24, 2014, 11:16:58 AM9/24/14
to Dimitri Liakhovitski, shiny-...@googlegroups.com
A single call to uiOutput should go into your ui.R file, where you want the download buttons to all appear. For example, uiOutput("downloadButtons"). Then in server.R, you define an output like so:

output$downloadButtons <- renderUI({
  lapply(1:N, function(i) {
    downloadButton(paste0("downloadData", i), paste0("Download No. ", i))
  })
})

And the observe code should also be right in server.R. Both of these blocks of code should be inside the shinyServer function.

Dimitri Liakhovitski

unread,
Sep 24, 2014, 12:54:58 PM9/24/14
to shiny-...@googlegroups.com, dl7...@gmail.com
Thanks a lot again, Joe.
In your code it says: "1:N" 
But what is N? I replaced it with input$NumRuns.
However, I am getting an error: 

Error in output$downloadButtons <- renderUI({ : object 'output' not found

Thanks again for your help!

Here is my code:

# For running:
library(shiny)
runApp(launch.browser = T)

# ui.R:
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(),
    output$downloadButtons <- renderUI({
      lapply(1:input$NumRuns, function(i) {
        downloadButton(paste0("downloadData", i), paste0("Download No. ", i))
      })
    })    
  ),
  
  mainPanel(
    tabsetPanel(
      tabPanel("Shows the 1st data frame",tableOutput("mydf"))
    )
  )
))


# server.R:
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
  })
  
  observe({
    lapply(1:input$NumRuns, function(i) {
      output[[paste0("downloadData", i)]] <- downloadHandler(...)
    })
  })
  
})


Dimitri Liakhovitski

unread,
Sep 30, 2014, 6:31:58 PM9/30/14
to shiny-...@googlegroups.com, dl7...@gmail.com
I am still struggling with it. Now it says: 

Error in output$downloadButtons <- renderUI({ : object 'output' not found

Help please?
Thank you!


My ui.R code is:

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(),
    output$downloadButtons <- renderUI({
      lapply(1:input$NumRuns, function(i) {
        downloadButton(paste0("downloadData", i), paste0("Download No. ", i))
      })
    })    
  ),
  
  mainPanel(
    tabsetPanel(
      tabPanel("Shows the 1st data frame",tableOutput("mydf"))
    )
  )
))


My "server.R" code is:
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
  })
  
  observe({
    lapply(1:input$NumRuns, function(i) {
      output[[paste0("downloadData", i)]] <- downloadHandler(
        filename = function() { paste(input$downloadData,i, " ",Sys.Date(),".csv",sep="") },
        content = function(file) {
          write.csv(myout()$dataframe[i],file,row.names=F)
    })
  })
  
})


Reply all
Reply to author
Forward
0 new messages