![](/img/trans.png)
[英]Using InvalidateLater and reactive events with observeEvent in Shiny
[英]Using a reactive value and a reactive expression to trigger an observeEvent
下面應用程序中的模塊包含一個具有兩個觸發器的觀察者:
input$trigger1
(在模塊服務器內呈現的 fileInput 的值)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(): 0
input$trigger1: NULL
。 當復選框被選中時, input$trigger1
仍然是NULL
但觀察者再次觸發並將input$trigger1: NULL
打印到控制台。 也許這是由於啟動時 DOM 中不存在trigger1
?
所以我嘗試同時調用模塊的服務器和 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: 根據該線程中的一篇文章,我發現由於input$trigger1
和trigger()
具有不同的類,因此將它們包裝在一個向量中可能是問題的根源(因為 R 將它們強制為相同的 class通過鏈接的帖子出來)。 所以我嘗試使用花括號代替:
observeEvent({input$trigger1; trigger2()}, print('the one with curly braces'), ignoreInit = T)
問題:該觀察者響應trigger2()
的變化,但不響應input$trigger1
的變化。 也許這與eventExpr
( trigger2()
) 的返回值有關,如果input$trigger1
更改,則不會更改,但是由於eventExpr
是反應式表達式,因此對input$trigger1
或trigger2()
的更改應該使其無效像在這篇文章中一樣觸發觀察者。
嘗試 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
更改時,reactiveVal 都會增加 1。 這樣,兩個觸發器都具有相同的 class 和長度(即 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 並且觀察者總是對兩個輸入做出響應,無論哪個首先更改。
默認情況下, observeEvent
設置了ignoreNULL = TRUE
。 正如您正確懷疑的那樣,雖然觀察者會觸發對{x;y}
任何部分的更改,但只有當整個表達式的值不是 NULL 時才會這樣做。 在這個例子中,只要y
是 NULL,無論你對x
做什么,觀察者都不會觸發。 這很容易通過設置ignoreNULL = FALSE
來解決。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.