Reputation: 6941
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))
}
Upvotes: 0
Views: 329
Reputation: 206446
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)
Upvotes: 1