The module in the app below contains an observer that has two triggers:
input$trigger1
(the value of a fileInput that is rendered inside the module server)trigger2()
(the expression returns the value of an actionButton in the main app that is fed to the module).I would like the observer to print some text to the console when triggered by changes in either of these two values.
Attempt 1: I tried wrapping the triggers in a vector:
observeEvent(c(input$trigger1, trigger2()), print('the one with the vector'), ignoreInit = T)
Problem: This observeEvent fires when the checkbox is checked.
I thought this might have something to do with the module's server being called instantaneously on app start-up whereas the module's UI is only rendered if input$check == TRUE
? I tried investigating this by printing the values of input$trigger1
and trigger2()
to the console using observe()
. On start-up, the values printed to the console are trigger2(): 0
input$trigger1: NULL
. When the checkbox is checked, input$trigger1
is still NULL
but the observer fires again and prints input$trigger1: NULL
to the console. Maybe this is due to trigger1
not existing in the DOM on start-up?
So I tried calling both the module's server and UI at the same time like so:
output$form = renderUI({req(input$check); modUI('mod')})
observeEvent(input$check, {
req(input$check)
callModule(modServer, 'mod', trigger2 = reactive(input$trigger2))
})
But the value of input$trigger1
still gets printed to the console twice, this time when the checkbox is checked. I don't know why because it's NULL
on both occasions.
Attempt 2: According to a post in this thread it occurred to me that since input$trigger1
and trigger()
have different classes, wrapping them in a vector might be the source of the problem (since R coerces them to the same class as pointed out by the linked post). So I tried using curly braces instead:
observeEvent({input$trigger1; trigger2()}, print('the one with curly braces'), ignoreInit = T)
Problem: This observer responds to changes in trigger2()
but not to changes in input$trigger1
. Maybe this has something to do with the return value of the eventExpr
( trigger2()
) not changing if input$trigger1
changes, but since the the eventExpr
is a reactive expression, a change to either input$trigger1
or trigger2()
should invalidate it to trigger the observer as in this post .
Attempt 3: I tried swapping the order of the triggers:
observeEvent({trigger2(); input$trigger1}, print('the one with curly braces with the order swapped around'), ignoreInit = T)
Problem: This time the observer responds to both triggers but only if input$trigger1 is changed first. Otherwise, it is unresponsive.
Here is the reproducible code:
library("shiny") # MODULE UI --------------------------------------------------------------- modUI = function(id) { ns = NS(id) uiOutput(ns('ui')) } # MODULE SERVER ----------------------------------------------------------- modServer = function(input, output, session, trigger2) { ns = session$ns output$ui = renderUI(fileInput(ns('trigger1'), 'Trigger1')) #OBSERVER 1 --------------------------------------------- observeEvent(c(input$trigger1, trigger2()), print('the one with the vector'), ignoreInit = T) # observe(print(list(`trigger2 (actionButton)` = trigger2()))) # observe(print(list(`trigger1 (fileInput)`= input$trigger1))) #OBSERVER 2 --------------------------------------------- # observeEvent({input$trigger1; trigger2()}, print('the one with curly braces'), ignoreInit = T) #OBSERVER 3 --------------------------------------------- # observeEvent({trigger2(); input$trigger1}, print('the one with curly braces with the order swapped around'), ignoreInit = T) } # MAIN APP ---------------------------------------------------------------- ui <- fluidPage( checkboxInput('check', 'Show'), uiOutput('form'), actionButton('trigger2', 'Trigger 2') ) server <- function(input, output, session) { output$form = renderUI({req(input$check); modUI('mod')}) # observe({ # req(input$check) # callModule(modServer, 'mod', trigger2 = reactive(input$trigger2)) # }) callModule(modServer, 'mod', trigger2 = reactive(input$trigger2)) } shinyApp(ui = ui, server = server)
I've tried everything I can think of to isolate the issue to no avail so any and all guidance would be very much appreciated.
UPDATE: Maybe it has something to do with the value of input$file
being a data.frame
? I tried replacing input$trigger1
with trigger1()
, a reactiveVal that increments by 1 each time input$trigger1
changes. This way both triggers have the same class and length (ie numeric(1)). The resulting observer works as expected:
modServer = function(input, output, session, trigger2) {
ns = session$ns
trigger1 = reactiveVal(0)
observeEvent(input$trigger1, trigger1(trigger1()+1))
observeEvent({trigger2(); trigger1()}, print('hello'), ignoreInit = T)
}
I would still like to understand why attempts 1-3 didn't work though so if anyone has any insight, please post a reply.
Explanation: As MattB pointed out, the actionButton initialises as 0 and the fileInput initialises as NULL. This means that the eventExpr for the observers in attempt 2 and 3 will evaluate to NULL as long as the input value on the last line of the expression is NULL. The eventExpr of the example in the linked post consists of a numericInput and a textInput. Since these initialise with values 0 and '' respectively, the eventExpr never returns NULL and the observer is always responsive to both inputs regardless of which is changed first.
observeEvent
has ignoreNULL = TRUE
set by default. As you correctly suspect, while the observer will trigger for a change to any part of {x;y}
, it will only do that if the value of that whole expression is not NULL. In this example, as long as y
is NULL, the observer won't trigger, no matter what you do to x
. This is easily fixed by setting ignoreNULL = FALSE
.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.