The module in the app below contains an observer that has two triggers:
- a reactive value
input$trigger1
(the value of a fileInput that is rendered inside the module server) - a reactive expression
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 (i.e. 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.
input$trigger1
andtrigger2()
as desired but it also causes it to fire when the checkbox is checked :( Any idea on why that's happening? SettingignoreInit == T
doesn't seem to help. – user51462