Reputation: 993
In shiny I am building a list of boxes and inside each there is a renderImage
like this:
images = ["i1.png", "i2.png", "i3.png"]
for(i in 1:3){
print(i)
q_list[[i]] = box(
width = 12,
status = "primary",
renderImage({
print(i)
img_path = images[i]
print(img_path)
list(src = img_path, alt = "This is alternate text")},
deleteFile = F)
)
}
unfortunately the boxes do not seem to register the i
in the loop. In the above, the first i
it prints is correct (1-2-3). But the second i
, the one inside renderImage
prints as 3 and the img_path
is i3.png
. Do I need to put another environment to renderImage
for this to work?
as requested in the comment here is a minimal example. the dahsboard displays the same graph (i3.png) in the 3 boxes:
rm(list = ls(all.names = TRUE))
library(shiny)
library(shinydashboard)
library(shinyWidgets)
library(shinybusy)
library(shinyjs)
options(shiny.error = browser)
options(shiny.fullstacktrace = TRUE)
options(shiny.trace = TRUE)
images = c("i1.png", "i2.png", "i3.png")
ui = dashboardPage(
sidebar = dashboardSidebar(disable = T),
body = dashboardBody(uiOutput("module_body")),
header = dashboardHeader(disable = T)
)
server = function(input, output, session){
imgs_fct = function(){
im_list = list()
for(i in 1:3){
print(i) # prints correctly
img_path = images[i] # prints correctly
print(img_path)
# store each box in im_list
im_list[[i]] = box(
width = 12,
status = "primary",
renderImage({
print(img_path) # prints "i3.png"
list(src = img_path, alt = "This is alternate text")}, deleteFile = F)
)
}
return(im_list)
}
output$module_body = renderUI({
theitems = tabItem(tabName = "xxx", fluidRow(column(width = 8, offset = 2, id = "form", imgs_fct())))}
)
}
# run the app
shinyApp(ui = ui, server = server)
Upvotes: 2
Views: 246
Reputation: 1237
As suggested in the comments, if you use lapply
in the imgs_fct
function, this will work as expected, with each plot rendered as expected rather than just the final plot for all of them. Note that images
is a vector of file paths to the images, so have assumed they're in the same directory as your Shiny app code:
imgs_fct = function(){
lapply(images,
function(x) {
box(
width = 12,
status = "primary",
renderImage({
list(src = x, alt = "This is alternate text")}, deleteFile = F)
)
})
}
The reason this works and a for
loop doesn't is because the counter you use to iterate (here i
) is shared across all the renderImage
calls as they're evaluated in the same environment (as they're within the same loop). What happens is, the calculation runs, and then when Shiny then tries to resolve any reactive dependencies at the end of this process, all the render
functions share the same reference i
which at this point is equal to 3, and so all render with the same image.
In contrast, with lapply
, each renderImage
is in a separate function call and hence are evaluated in a separate environment, where each function has as an argument a different image file path. Hence, these render separately as you expect. You could probably wrap the contents of your for
loop in local({})
but I personally like the lapply
approach as it seems more 'R'-y.
Upvotes: 2