简体   繁体   English

使用反应式值和反应式表达式来触发观察事件

[英]Using a reactive value and a reactive expression to trigger an observeEvent

The module in the app below contains an observer that has two triggers:下面应用程序中的模块包含一个具有两个触发器的观察者:

  1. a reactive value input$trigger1 (the value of a fileInput that is rendered inside the module server)一个反应值input$trigger1 (在模块服务器内呈现的 fileInput 的值)
  2. a reactive expression trigger2() (the expression returns the value of an actionButton in the main app that is fed to the module).反应式表达式trigger2() (表达式返回主应用程序中提供给模块的 actionButton 的值)。

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:尝试 1:我尝试将触发器包装在向量中:

  observeEvent(c(input$trigger1, trigger2()), print('the one with the vector'), ignoreInit = T)

Problem: This observeEvent fires when the checkbox is checked.问题:当复选框被选中时,这个 observeEvent 被触发。

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 ?我认为这可能与在应用程序启动时立即调用模块的服务器有关,而模块的 UI 仅在input$check == TRUE时才呈现? I tried investigating this by printing the values of input$trigger1 and trigger2() to the console using observe() .我尝试通过使用observe()input$trigger1trigger2()的值打印到控制台来对此进行调查。 On start-up, the values printed to the console are trigger2(): 0 input$trigger1: NULL .启动时,打印到控制台的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.当复选框被选中时, input$trigger1仍然是NULL但观察者再次触发并将input$trigger1: NULL打印到控制台。 Maybe this is due to trigger1 not existing in the DOM on start-up?也许这是由于启动时 DOM 中不存在trigger1

So I tried calling both the module's server and UI at the same time like so:所以我尝试同时调用模块的服务器和 UI,如下所示:

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.但是input$trigger1的值仍然会打印到控制台两次,这一次是在选中复选框时。 I don't know why because it's NULL on both occasions.我不知道为什么,因为两次都是NULL


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).尝试 2: 根据该线程中的一篇文章,我发现由于input$trigger1trigger()具有不同的类,因此将它们包装在一个向量中可能是问题的根源(因为 R 将它们强制为相同的 class通过链接的帖子出来)。 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 .问题:该观察者响应trigger2()的变化,但不响应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 .也许这与eventExpr ( trigger2() ) 的返回值有关,如果input$trigger1更改,则不会更改,但是由于eventExpr是反应式表达式,因此对input$trigger1trigger2()的更改应该使其无效像在这篇文章中一样触发观察者。


Attempt 3: I tried swapping the order of the triggers:尝试 3:我尝试交换触发器的顺序:

  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.问题:这一次观察者响应两个触发器,但前提input$trigger1 首先被改变。 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 ?更新:也许它与input$file作为data.frame的值有关? I tried replacing input$trigger1 with trigger1() , a reactiveVal that increments by 1 each time input$trigger1 changes.我尝试用trigger1()替换input$trigger1 ,每次input$trigger1更改时,reactiveVal 都会增加 1。 This way both triggers have the same class and length (ie numeric(1)).这样,两个触发器都具有相同的 class 和长度(即 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.我仍然想了解为什么尝试 1-3 不起作用,所以如果有人有任何见解,请发表回复。


Explanation: As MattB pointed out, the actionButton initialises as 0 and the fileInput initialises as NULL.说明:正如 MattB 指出的,actionButton 初始化为 0,fileInput 初始化为 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.这意味着,只要表达式最后一行的输入值为 NULL,尝试 2 和 3 中的观察者的 eventExpr 将计算为 NULL。 The eventExpr of the example in the linked post consists of a numericInput and a textInput.链接帖子中示例的 eventExpr 由 numericInput 和 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.由于它们分别使用值 0 和 '' 进行初始化,因此 eventExpr 永远不会返回 NULL 并且观察者总是对两个输入做出响应,无论哪个首先更改。

observeEvent has ignoreNULL = TRUE set by default.默认情况下, observeEvent设置了ignoreNULL = TRUE 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.正如您正确怀疑的那样,虽然观察者会触发对{x;y}任何部分的更改,但只有当整个表达式的值不是 NULL 时才会这样做。 In this example, as long as y is NULL, the observer won't trigger, no matter what you do to x .在这个例子中,只要y是 NULL,无论你对x做什么,观察者都不会触发。 This is easily fixed by setting ignoreNULL = FALSE .这很容易通过设置ignoreNULL = FALSE来解决。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM