0

I fixed a bug, but I do not understand why it happened in the first place. Can anyone help clarify?

I am building an interactive data explorer app, with each figure contained in its own module. Under my first approach ("old version that does not work") I first build a look-up list and then use it to display the correct control and plot.

However, no plot is displayed until you have clicked on all the options in the figure selection list.

I fixed this by switching to the "new version that works" without an intermediate look-up list. But do not understand why this happened.

Minimal reproducible example below, sorry about the length.

# required packages
library(shiny)
library(tidyverse)

## ui ----
ui = fluidPage(
  sidebarLayout(
    sidebarPanel(
      uiOutput("plot_controls"),
      width=3
    ),
    mainPanel(
      selectInput("selected_plot", "Select figure:", choices = c("Histogram", "Scatter")),
      plotOutput("plot_plot", height = "700px"),
      width=9
)))

## server ----
server = function(input, output, session) {
  data(starwars)
  ### modules ----
  fig_histogram = callModule(figure_histogram_plot, "histogram", datafile = starwars)
  fig_scatter = callModule(figure_Scatter_Plot, "scatter", datafile = starwars)
  module_list = list( fig_histogram, fig_scatter )

  ### old version that does not work ----
  resource.map_text_to_plot = reactive({
    map = list("Histogram" = module_list[[1]]$plot(), "Scatter" = module_list[[2]]$plot())
  })
  output$plot_plot = renderPlot({ resource.map_text_to_plot()[input$selected_plot] })

  resource.map_text_to_control = reactive({
    map = list("Histogram" = module_list[[1]]$control, "Scatter" = module_list[[2]]$control)
  })
  output$plot_controls = renderUI({ resource.map_text_to_control()[input$selected_plot] })

  ### new version that works ----
  # output$plot_controls = renderUI({
  #   for(module in module_list)
  #     if(module$text == input$selected_plot)
  #       return(module$control)
  # })
  # 
  # output$plot_plot = renderPlot({
  #   for(module in module_list)
  #     if(module$text == input$selected_plot)
  #       return(module$plot())
  # })
}
shinyApp(ui = ui, server = server)

The figure modules are as follows:

### histogram - server ----
figure_histogram_plot = function(input, output, session, datafile){
  text = "Histogram"
  control = sliderInput(session$ns("num_bins"), "Number of bins", min = 5, max = 50, value = 30)

  plot = reactive({
    p = ggplot(data = datafile) + geom_histogram(aes_string(x = "height"), bins = input$num_bins)
    return(p)
  })

  return(list(text = text, plot = plot, control = control))
}

### scatter - server ----
figure_Scatter_Plot = function(input, output, session, datafile){
  text = "Scatter"
  control = radioButtons(session$ns("plot_design"), "Plot design", choices = c("points", "lines", "both"))

  plot = reactive({
    p = ggplot(data = datafile)

    if(input$plot_design != "lines")
      p = p + geom_point(aes_string( x = "mass", y = "height" ))
    if(input$plot_design != "points")
      p = p + geom_line(aes_string( x = "mass", y = "height" ))

    return(p)
  })

  return(list(text = text, plot = plot, control = control))
}
Simon.S.A.
  • 6,240
  • 7
  • 22
  • 41
  • 1
    When asking for help, you should include a simple [reproducible example](https://stackoverflow.com/questions/5963269/how-to-make-a-great-r-reproducible-example) with sample input and desired output that can be used to test and verify possible solutions. It's not clear what's going on from the code chunks provided. We need something we can actually run and test with. It doesn't have to be (not should it be) your whole application. Just a minimal example that recreates the problem. – MrFlick Jul 12 '18 at 00:05

1 Answers1

1

So I think the problem is when exactly you are evaluating the reactive element. When you use () to "call" a reactive element, it's not really reactive any more. When you set up the list, use

  resource.map_text_to_plot = reactive({
    map = list("Histogram" = module_list[[1]]$plot, 
               "Scatter" = module_list[[2]]$plot)
    map
  })

and when you set up the renderPlot, use

  output$plot_plot = renderPlot({ 
    resource.map_text_to_plot()[[input$selected_plot]]() })

Note we removed the () from the list, and put it in the render function instead.

Also, your controls aren't being initialized before the first plot is drawn, so the input$plot_design value can be NULL and you don't seem to check for that which causes a problem at the first draw (you will briefly see an error most likely)

MrFlick
  • 195,160
  • 17
  • 277
  • 295
  • I am aware of the error. I usually use `validate(need(... ,message=FALSE))` to prevent the plot drawing until the controls have initialized. But I stripped this out to make the example more compact. Is there a better way to get my controls to initialize prior to drawing? – Simon.S.A. Jul 12 '18 at 22:42