繁体   English   中英

你能告诉 MutationObserver 只听特定的输入,例如 select 选择器输入中的一整组选择吗?

[英]Can you tell a MutationObserver to only listen to specific inputs, e.g. to select an entire group of choices in a pickerInput?

这是受启发和跟进Is there a way to select a whole group of selection 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中侦听并且仅侦听突变?

在我的实际用例中,我希望在两个不同的选项卡(来自shinydashboard )上有两个不同的pickerInput ,我希望有这个功能。 所以也许告诉MutationObserver在哪个选项卡上查看就足够了? (请参阅How do I use shinyjs to identify if tab is active in shiny? ,但作为 JS 初学者,我不确定如何使用它)。

更新

通过将req(input$sidebar == "tab_name")添加到observeEvent部分,我设法在我的用例中稍微接近了一点。 现在他们都更新了正确的pickerInput ,但是对于第二个,每次我点击第一个pickerInput中的组 header 后,该功能只工作一次。

所以,还是没有。 我正在尝试使用来自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% my_choices[[input$group_select[[1]]]])]
    }else{
      sel <- union(input[[names(input$group_select)]],my_choices[[input$group_select[[1]]]])
    }
    updatePickerInput(session,names(input$group_select),selected = sel)
  })
  

原始答案的作者在这里。 实际上,您需要按如下方式调整代码:

  1. 您的clickHandler应该返回单击的pickerInput的 id,以便Shiny可以知道要更新哪个pickerInput
  2. 您的observer必须在不同的节点上侦听。 class .innerfluidPage布局配合得很好,因为显然.inner class 在加载 DOM 时存在,但shinydashboard一起使用(我猜当 JS 触发时这些部件尚未加载)。 最简单的是听body (肯定会在那里),但这可能是一项昂贵的操作,b/ca 更窄的目标可能需要更少的资源。

话虽如此,一个可行的解决方案看起来像这样(注意您可以添加尽可能多的pickerInputs ,并且处理程序应该按预期工作):

library(shiny)
library(shinyWidgets)
library(shinydashboard)
library(purrr)

js <- HTML("
$(function() {
  let observer = new MutationObserver(callback);

  function clickHandler(evt) {
    let id = $(this).parents('.bootstrap-select').children('select').attr('id');
    let res = {};
    res[id] = $(this).children('span').text();
    Shiny.setInputValue('group_select', res);
  }

  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($('body')[0], options);
})
")

choices <- list("A" = c(1, 2, 3, 4, 5), "B" = c(6, 7, 8, 9, 10))

ui <- dashboardPage(
   dashboardHeader(),
   dashboardSidebar(pickerInput("test1", choices = choices, multiple = TRUE),
                     pickerInput("test2", choices = choices, multiple = TRUE)),
   dashboardBody(tags$head(tags$script(js)),
                 verbatimTextOutput("testOutput")),
   title = "Multipicker"
)

server <- function(input, output, session) {
   output$testOutput <- renderPrint({
      input$group_select
   })
   
   observeEvent(input$group_select, {
      req(input$group_select)
      iwalk(input$group_select, ~ updatePickerInput(session, .y, selected = choices[[.x]]))
   })
}

shinyApp(ui = ui, server = server)

暂无
暂无

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

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