Reputation: 41220
This is an old Shiny issue: shiny app will not work if the same "output" is used two times in Ui.R
A simple example :
library(shiny)
## app.R ##
server <- function(input, output) {
output$distPlot <- renderPlot({
hist(rnorm(input$obs), col = 'darkgray', border = 'white')
})
}
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
sliderInput("obs", "Number of observations:", min = 10, max = 500, value = 100)
),
mainPanel(plotOutput("distPlot")
# If the next line is commented out, histogram appears correctly
,plotOutput("distPlot")
)
)
)
shinyApp(ui = ui, server = server)
This doesn't work because :
Shiny doesn't support multiple outputs with the same name. This code would generate HTML where two elements have the same ID, which is invalid HTML. See this or this.
The result looks fine, but isn't what is expected (no histogram shown) :
The ShinyApp seems to be working normally :
Listening on http ://127.0.0.1:7081
Although I know this issue, I have already been tricked a few times in complex UIs, and I was wondering if there was a way to output an automatic warning in the console on this?
For example :
Warning message: Output 'distPlot' is used twice in UI - this is not supported and might lead to unexpected results
Thanks for sharing your solutions on this issue!
#EDIT : This has been solved from Shiny 1.8.1 on.
Just use shiny::devmode(TRUE)
Thanks @ismirsehregal for the hint
Upvotes: 5
Views: 1182
Reputation: 41220
Under the assumption that most Shiny UI outputs follow this pattern :
outputTypeOutput("outputname")
I wrote the checkShinyOutput
function which can be called before UI definition in UI script :
checkShinyOutput <- function(){
tryCatch({
parsed <- getParseData(parse(file = rstudioapi::getSourceEditorContext()$path))
shinyOutput <- parsed[parsed$token=='SYMBOL_FUNCTION_CALL'& grepl("^[a-z]+[A-z]+Output$",parsed$text),]
shinyOutput <- merge(shinyOutput,parsed,by='line1')
shinyOutput <- shinyOutput[shinyOutput$token.y == "STR_CONST",]
warn <- table(shinyOutput$text.y)
warn <- warn[warn>=2]
warnname <- names(warn)
if (length(warn>1)) {
warning(mapply(function(warn,nb){paste("Output",warn,"is used",nb,"times")},warnname,warn))
}
},
error = function(){},
warning = function(cond) {
message("Shiny UI : check following warnings to avoid unexpected UI behaviour")
message(cond)
}
)
}
This appears in Console when the app is run from RStudio :
> runApp('test')
Shiny UI : check following warnings to avoid unexpected UI behaviour
Output "distPlot" is used 2 times
Listening on http://127.0.0.1:7414
#EDIT : from Shiny 1.8.1, this isn't anymore needed as Shiny throws an error.
Just use shiny::devmode(TRUE)
Thanks @ismirsehregal for the hint
Upvotes: 2
Reputation: 20329
I think that it may be quite some task to print it out to the R console. But if you open your app and go to the JS console you should see an error message:
Feedback as requested (here in the answer to allow for formatting purposes):
Your code covers basic cases, but there are some edge cases which you may be overlooking (this list does not claim to be exhaustive):
(and this are just 2 cases I can quickly think of - and they are not completely unrealistic to be honest)
Overall, I think it will be quite some work to cover all cases, while a quick look into the JS console reveals all you need, just in a different environment. IMHO completeness beats convenience.
outname <- "distPlot"
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
sliderInput("obs", "Number of observations:", min = 10, max = 500, value = 100)
),
mainPanel(plotOutput("distPlot"),
plotOutput(
"distPlot" # not covered b/c in other line
),
plotOutput(outname) # not covered b/c in variable
)
)
)
Upvotes: 2