【发布时间】:2020-05-23 05:46:37
【问题描述】:
下面应用程序中的模块包含一个具有两个触发器的观察者:
- 一个反应值
input$trigger1(文件输入的值 在模块服务器内呈现) - 反应式表达式
trigger2()(该表达式返回主应用程序中提供给模块的actionButton 的值)。
我希望观察者在被这两个值中的任何一个更改触发时将一些文本打印到控制台。
尝试 1:我尝试将触发器包装在向量中:
observeEvent(c(input$trigger1, trigger2()), print('the one with the vector'), ignoreInit = T)
问题:当复选框被选中时,这个 observeEvent 被触发。
我认为这可能与模块的服务器在应用程序启动时被立即调用有关,而模块的 UI 仅在 input$check == TRUE? 时才会呈现。我尝试通过使用observe() 将input$trigger1 和trigger2() 的值打印到控制台来对此进行调查。在启动时,打印到控制台的值为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$trigger1 和 trigger() 有不同的类,将它们包装在向量中可能是问题的根源(因为 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$file 是data.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$trigger1和trigger2(),但它也会在选中复选框时触发它:( 知道为什么会发生这种情况吗?设置ignoreInit == T似乎没有帮助。