这是受Is there a way to select an entire group of choices on a pickerInput from shinyWidgets?的启发,也是它的后续
所选的答案对于单个pickerInput
非常有效,但是当第二个(“稍后”)答案出现在同一个应用程序中时,问题就会出现。例如,假设有两个pickerInput
:
library(shiny)
library(shinyWidgets)
js <- HTML("
$(function() {
let observer = new MutationObserver(callback);
function clickHandler(evt) {
Shiny.setInputValue('group_select', $(this).children('span').text(),{priority: \"event\"});
}
function callback(mutations) {
for (let mutation of mutations) {
if (mutation.type === 'childList') {
$('.dropdown-header').on('click', clickHandler).css('cursor', 'pointer');
}
}
}
let options = {
childList: true,
};
observer.observe($('.inner')[0], options);
})
")
choices <- list("A" = c(1, 2, 3, 4, 5), "B" = c(6, 7, 8, 9, 10), "C" = c(11,12,13,14,15))
ui <- fluidPage(
tags$head(tags$script(js)),
pickerInput("test", choices = choices, multiple = TRUE,options = list('actions-box' = TRUE)),
textOutput("testOutput"),
pickerInput("test_2", choices = choices, multiple = TRUE,options = list('actions-box' = TRUE)),
textOutput("testOutput_2")
)
server <- function(input, output, session) {
output$testOutput <- renderText({paste(input$test)})
output$testOutput_2 <- renderText({paste(input$test_2)})
observeEvent(input$group_select, {
req(input$group_select)
if(all(choices[[input$group_select]] %in% input$test_2)){
sel <- input$test_2[!(input$test_2 %in% choices[[input$group_select]])]
}else{
sel <- union(input$test_2, choices[[input$group_select]])
}
updatePickerInput(session, "test_2", selected = sel)
})
}
shinyApp(ui = ui, server = server)
这样,点击第一个pickerInput
中的一个组会更新第二个组,而点击第二个组则什么也不做。如何告诉MutationObserver
只监听第二个pickerInput
中的突变?
在我的实际用例中,我在两个不同的选项卡上有两个不同的pickerInput
(从shinydashboard
开始),我希望有这个功能。因此,也许告诉MutationObserver
在哪个选项卡上查看就足够了?(参见How do I use shinyjs to identify if tab is active in shiny?,但作为一个JS初学者,我真的不知道如何使用它)。
更新
在我的用例中,我已经通过将req(input$sidebar == "tab_name")
添加到observeEvent
部分来设法稍微接近。现在它们都更新了正确的pickerInput
,但是对于第二个,每次我点击第一个pickerInput
中的组头后,该功能只工作一次。
所以,还是不行。我试图使用shinydashboard
中的制表符得到一个可重现的示例,但由于某种原因,当我将它们引入MWE时,整个mutationObserver
完全停止工作。
更新2
@thothal的回答奏效了。我将observeEvent
部分重写为以下内容以获得相同的功能:
observeEvent(input$group_select, {
req(input$group_select)
if(all(choices[[input$group_select[[1]]]] %in% input[[names(input$group_select)]])){
sel <- input[[names(input$group_select)]][!(input[[names(input$group_select)]] %in% choices[[input$group_select[[1]]]])]
}else{
sel <- union(input[[names(input$group_select)]],choices[[input$group_select[[1]]]])
}
updatePickerInput(session,names(input$group_select),selected = sel)
})
1条答案
按热度按时间pod7payv1#
原创答案的作者在这里。实际上,你需要调整代码如下:
1.您的
clickHandler
应该返回单击的pickerInput
的id,以便Shiny
可以知道要更新哪个pickerInput
。1.您的
observer
必须侦听不同的节点。类.inner
在fluidPage
布局中工作得很好,因为显然.inner
类在DOM加载时就存在了,但在shinydashboard
中不存在(我猜当JS触发时,这些部分还没有加载)。最简单的方法是监听body
(它肯定会在那里),但这可能是一个昂贵的操作,b/c更窄的目标可能需要更少的资源。话虽如此,一个工作解决方案看起来像这样(N.B. 您可以添加尽可能多的
pickerInputs
,处理程序应该像预期的那样工作):更新
为了允许在第二次单击时取消选择并保持选择持久,我们必须进行2次更改:
1.告诉JS发送每次点击(而不仅仅是新的点击):
1.适配
observer
现在的行为是,当(且仅当)没有选择任何子项时,单击将添加所有子项。如果至少选择了一个子项,则取消选择所有子项。最后,选择堆积起来。也就是说,以前的选择不会被丢弃,而是将新的选择添加到以前的选择中。