Dynamic Reactive UI


#1

Hi,
I’m trying to combine two tutorials to create a dynamic set of reactive elements but can’t get it to work. I’d like to combine this tutorial https://gist.github.com/wch/5436415/ with the standard example of a histogram with a slider for the bins.
So one slider controls how many panels are created and in each panel there is a slider and a histogram and that slider controls the bins of the histogram.

This is the dynamic plot example:

shinyUI(pageWithSidebar(
  
  headerPanel("Dynamic number of plots"),
  
  sidebarPanel(
    sliderInput("n", "Number of plots", value=1, min=1, max=5)
  ),
  
  mainPanel(
    uiOutput("plots") # This is the dynamic UI for the plots
  )
))
max_plots <- 5

shinyServer(function(input, output) {
  
  # Insert the right number of plot output objects into the web page
  output$plots <- renderUI({
    plot_output_list <- lapply(1:input$n, function(i) {
      plotname <- paste("plot", i, sep="")
      plotOutput(plotname, height = 280, width = 250)
    })
    
    # Convert the list to a tagList - this is necessary for the list of items
    # to display properly.
    do.call(tagList, plot_output_list)
  })
  
  # Call renderPlot for each one. Plots are only actually generated when they
  # are visible on the web page.
  for (i in 1:max_plots) {
    # Need local so that each item gets its own number. Without it, the value
    # of i in the renderPlot() will be the same across all instances, because
    # of when the expression is evaluated.
    local({
      my_i <- i
      plotname <- paste("plot", my_i, sep="")
      
      output[[plotname]] <- renderPlot({
        plot(1:my_i, 1:my_i, xlim = c(1, max_plots), ylim = c(1, max_plots), main = paste("1:", my_i, ".  n is ", input$n, sep = ""))
      })
    })
  }
})

and I’d like to combine it with something like this:

fluidRow(column(4,
                        sliderInput(inputId="bins", 
                                    "Number of bins:",
                                    min = 1,
                                    max = 50,
                                    value = 30)),
                 
                 column(8, renderPlot({
                   
                   x    <- faithful[, 2] 
                   bins <- seq(min(x), max(x), length.out = input$bins + 1)
                   
                   # draw the histogram with the specified number of bins
                   hist(x, breaks = bins, col = 'darkgray', border = 'white')
                   
                   
                   
                 })))

But when I replace renderPlot with renderUI and insert the fluidRow nothing is rendered at all. Any help solving this would be greatly appreciated.
Thanks!


#2

Generally, you put uiOutput/plotOutput in the UI, and renderUI/renderPlot in the server; you don’t put renderPlot directly into the UI. You should put the uiOutput from the original example back in your UI, move the renderPlot to the server (and assign it to an output$whateverId), and then add plotOutput("whateverId") to the UI.


#3

Thanks for the pointer. I got it working. For anybody else interested here is the code:

shinyServer(function(input, output) {
  output$a_out <- renderPrint({
    res <- lapply(1:5, function(i)
      input[[paste0('n', i)]])
    str(setNames(res, paste0('a', 1:5)))
  })
  
  lapply(1:5, function(i) {
    output[[paste0('c', i)]] <- renderPlot({
      hist(
        AirPassengers,
        breaks = input[[paste0('n', i)]],
        col = 'darkgray',
        border = 'white'
      )
    })
  })

})
shinyUI(fluidPage(title = 'Creating a UI from a loop',
                  
  lapply(1:5, function(i) {
    fluidRow(column(4,
                    wellPanel(
                      sliderInput(
                        paste0("n", i),
                        "Number of bins",
                        value = 30,
                        min = 1,
                        max = 50
                      )
                    )),
             column(8, wellPanel(plotOutput(paste0(
               'c', i
             )))))
  })))