The app below contains a selectInput (input$month) and a button, Show Modal, that launches a modal window when clicked. The modal contains a button, Add UI, that inserts some text, Element x, where x is the value of a counter that increases by 1 each time Add UI is clicked.
Screenshot of app on startup with the modal window opened:
The app is modularised so that UI associated with the modal is rendered by function modUI and the corresponding server logic is defined in function modServer. modUI is rerendered each time input$month changes to overwrite previously inserted text elements.
I require the first block of text to be inserted programmatically, so that when the app loads OR the user changes input$month, Element 1 is already rendered in the modal. To do this, I tried to get the insertUI observer to fire if input$add_ui greater than or equal to 0 - i.e. observeEvent(if(req(input$add_ui) >= 0) TRUE else return(), { #insertUI expr }). However, this doesn't work and I don't understand why. Since observers are eagerly evaluated, shouldn't this observer fire after input$add_ui has finished initialising?
The counter must also be reset to 0 each time input$month changes so that inserted text elements start at Element 1. To do this, I included the following observer in modServer:
observe({
req(input$add_ui == 0) #checking that modUI has finished rerendering
print(paste('month changed to:', month, 'resetting counter'));
counter(0)
})
Lastly, I am relatively new to modules and was wondering if someone could explain why counter() is not reset automatically whenever modServer is called via observe(callModule(modServer, 'hi', month = input$month))? Why does its value persist if the module has its own environment?
I would be grateful for any help as I have been stuck on this for a while.
Code to reproduce the above:
library(shiny)
library(shinyBS)
#MODULE UI ----
modUI <- function(id) {
ns <- NS(id)
tagList(
actionButton(ns("show_modal"), "Show modal"),
bsModal(
id = ns('modal'),
trigger = ns('show_modal'),
actionButton(ns("add_ui"), "Add UI"),
tags$div(id = ns("placeholder1"))
)
)
}
#MODULE SERVER ----
modServer <- function(input, output, session, month) {
ns <- session$ns
counter <- reactiveVal(0)
# Observer to insert UI element
observeEvent(if(req(input$add_ui) >= 0) TRUE else return(), {
counter(counter() + 1)
insertUI(
selector = paste0("#", ns("placeholder1")),
ui = tags$div(paste('Element', counter()))
)
})
# Reset counter() if month is changed
observe({
print(paste('month changed to:', month(), 'resetting counter'));
counter(0)
}, ignoreInit = T)
# Print
observe({ print(paste('input$add_ui:', input$add_ui, 'counter:', counter())) })
}
#MAIN UI ----
ui <- fluidPage(
tagList(
selectInput('month', 'Month', month.abb),
uiOutput('modal_ui')
)
)
#MAIN SERVER ----
server <- function(input, output, session) {
#Call modUI if input$month is changed
callModule(modServer, 'hi', month = reactive(input$month)) #previously observe(callModule(modServer, 'hi', month = input$month)), why isn't this allowed?
#Rerender modUI if input$month is changed
output$modal_ui <- renderUI({
input$month
modUI('hi')
})
observe(print(input$month))
}
shinyApp(ui = ui, server = server)