【问题标题】:Using a reactive value and a reactive expression to trigger an observeEvent使用反应式值和反应式表达式来触发观察事件
【发布时间】:2020-05-23 05:46:37
【问题描述】:

下面应用程序中的模块包含一个具有两个触发器的观察者:

  1. 一个反应值input$trigger1(文件输入的值 在模块服务器内呈现)
  2. 反应式表达式trigger2()(该表达式返回主应用程序中提供给模块的actionButton 的值)。

我希望观察者在被这两个值中的任何一个更改触发时将一些文本打印到控制台。

尝试 1:我尝试将触发器包装在向量中:

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

问题:当复选框被选中时,这个 observeEvent 被触发。

我认为这可能与模块的服务器在应用程序启动时被立即调用有关,而模块的 UI 仅在 input$check == TRUE? 时才会呈现。我尝试通过使用observe()input$trigger1trigger2() 的值打印到控制台来对此进行调查。在启动时,打印到控制台的值为trigger2(): 0input$trigger1: NULL。当复选框被选中时,input$trigger1 仍然是NULL,但观察者再次触发并将input$trigger1: NULL 打印到控制台。可能是因为 trigger1 在启动时不存在于 DOM 中?

所以我尝试像这样同时调用模块的服务器和 UI:

output$form = renderUI({req(input$check); modUI('mod')})

observeEvent(input$check, {
    req(input$check)
    callModule(modServer, 'mod', trigger2 = reactive(input$trigger2))
})

但是input$trigger1 的值仍然会打印到控制台两次,这一次是在选中复选框时。我不知道为什么,因为两次都是NULL


尝试 2: According to a post in this thread 我突然想到,由于 input$trigger1trigger() 有不同的类,将它们包装在向量中可能是问题的根源(因为 R 强制它们到链接帖子指出的同一类)。所以我尝试使用花括号:

  observeEvent({input$trigger1; trigger2()}, print('the one with curly braces'), ignoreInit = T)

问题:此观察者响应 trigger2() 中的更改,但不响应 input$trigger1 中的更改。也许这与eventExpr (trigger2()) 的返回值有关@ 应该使其无效以触发观察者,如this post


尝试 3:我尝试交换触发器的顺序:

  observeEvent({trigger2(); input$trigger1}, print('the one with curly braces with the order swapped around'), ignoreInit = T)

问题:这一次观察者对两个触发器都有响应,但只有在 input$trigger1 首先被更改时。否则,它没有响应。


这是可重现的代码:

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)

我已经尝试了我能想到的一切来隔离问题,但无济于事,因此非常感谢任何和所有指导。

更新:也许这与input$filedata.frame 的价值有关?我尝试用trigger1() 替换input$trigger1,这是一个每次input$trigger1 更改时递增1 的reactiveVal。这样,两个触发器都具有相同的类和长度(即 numeric(1))。结果观察者按预期工作:

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)

}

我仍然想了解为什么尝试 1-3 不起作用,所以如果有人有任何见解,请发表回复。


解释: 正如 MattB 所指出的,actionButton 初始化为 0,而 fileInput 初始化为 NULL。这意味着,只要表达式最后一行的输入值为 NULL,尝试 2 和 3 中的观察者的 eventExpr 将评估为 NULL。链接帖子中示例的 eventExpr 由 numericInput 和 textInput 组成。由于它们分别使用值 0 和 '' 进行初始化,因此 eventExpr 永远不会返回 NULL 并且观察者始终对两个输入做出响应,无论哪个输入先更改。

【问题讨论】:

  • 您是否尝试过将触发事件封装在一个列表中?
  • 谢谢伯蒂尔,这几乎可以工作了!将触发器包装在列表中会使观察者根据需要响应input$trigger1trigger2(),但它也会在选中复选框时触发它:( 知道为什么会发生这种情况吗?设置ignoreInit == T 似乎没有帮助。

标签: r shiny


【解决方案1】:

observeEvent 默认设置了ignoreNULL = TRUE。正如您正确怀疑的那样,虽然观察者会触发对{x;y} 任何部分的更改,但只有在整个表达式 的值不为NULL 时才会这样做。在这个例子中,只要y 为NULL,观察者就不会触发,无论你对x 做什么。通过设置ignoreNULL = FALSE 可以轻松解决此问题。

【讨论】:

  • 感谢@MattB,这是有道理的,但是当input$trigger1NULL 时,设置ignoreNULL = TRUE 不会使观察者3 响应trigger2()。什么可以解释这一点和观察者 2 的行为?
  • 你的意思是设置ignoreNULL = FALSE
  • +1 对不起,我的错,我在尝试ignoreNULL = TignoreNULL == FALSE。确实使所有三个观察者都对两个触发器做出响应,但它也会在选中复选框时触发它们。这是因为模块的服务器在其 UI 之前被调用的方式(因为 UI 仅在选中复选框时被调用)?我尝试在选中复选框时调用模块的服务器(即,observeEvent(input$check,{ req(input$check); callModule(modServer, 'mod', trigger2 = reactive(input$trigger2)) }))但它没有没用。
  • 恐怕帮不上那个忙。我看不到任何会导致这种情况的东西(不过我之前没有使用过闪亮的模块)。
  • 不用担心,这可能与我在这种情况下调用/渲染模块的方式有关。我接受了你的回答,因为它解释了为什么观察者 2 和 3 不起作用。感谢您的帮助,我完全忽略了 2 和 3 的 eventExpr 返回 NULL 的事实。
猜你喜欢
  • 2019-03-31
  • 2018-11-28
  • 1970-01-01
  • 2013-08-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-15
  • 1970-01-01
相关资源
最近更新 更多