Solved here by Le Zhang:
This is because render events are controlled by Javascripts in Shiny. By default, if the dynamic rendering UI is hidden, in your case is the uiOutput element, Shiny would not render it unless it is visible. The term visible here is more than just toggle the css display state to be block. It needs to hear instructions from javascript listeners. In this case, it explicitly listens to a "shown" type of listener. So the way to solve it is in addition to change the display, you also need to trigger the shown event.
Method 1, just use {shinyjs} package, the easiest way
shinyjs::onclick(
"modal",
shinyjs::show("my_modal")
)
Method 2, use jquery, which is what behind {shinyjs}
shinyjs::onclick(
"modal",
shinyjs::runjs("$('#my_modal').show(); $('#new_ui').trigger('shown');")
)
Method 3, use native js, in case you do not want to use jquery
shinyjs::onclick(
"modal",
shinyjs::runjs(
"
document.getElementById('my_modal').style.display = 'block';
document.getElementById('new_ui').dispatchEvent(new Event('shown', {namespace: '', bubbles: true}));
"
)
)
Full code
library(shiny)
library(shinyjs)
ui <- fluidPage(
tags$head(
tags$link(rel="stylesheet", href="https://www.w3schools.com/w3css/4/w3.css")
),
useShinyjs(),
actionButton("modal", "open modal"),
div(
id = "my_modal",
class = "w3-modal",
div(
class = "w3-modal-content w3-animate-zoom",
div(
class = "w3-container w3-padding",
actionButton("generate_ui", "generate UI"),
uiOutput("new_ui"),
div(
class="w3-container w3-padding",
tags$button(class="w3-button w3-right w3-white w3-border",
onclick="document.getElementById('my_modal').style.display='none'",
"Close")
)
)
)
),
tags$script(
HTML( '// Get the modal
var modal = document.getElementById("my_modal");
// When the user clicks anywhere outside of the modal, close it
window.onclick = function(event) {
if (event.target == modal) {
modal.style.display = "none";
}
}')
)
)
server <- function(input, output) {
shinyjs::onclick(
"modal",
# shinyjs::runjs(
# "
# document.getElementById('my_modal').style.display = 'block';
# document.getElementById('new_ui').dispatchEvent(new Event('shown', {namespace: '', bubbles: true}));
# "
# )
#
# shinyjs::runjs("$('#my_modal').show(); $('#new_ui').trigger('shown');")
shinyjs::show("my_modal")
)
observeEvent(input$generate_ui, {
print("hello from modal")
output$new_ui <- renderUI({
tagList(
p("This is the new UI")
)
})
})
}
shinyApp(ui = ui, server = server)