Shiny:根据选择更新 selectizeInput 选择

Posted

技术标签:

【中文标题】Shiny:根据选择更新 selectizeInput 选择【英文标题】:Shiny: update selectizeInput choices based on selected 【发布时间】:2019-07-31 09:05:11 【问题描述】:

我正在尝试根据当前的selected 选择更新selectizeInputchoices。这是我的尝试(导致循环):

library(shiny)
run_ui <- function() 

  ui <- selectizeInput('words', 'Search words:', choices = NULL, selected = NULL, multiple = TRUE, options = NULL)

  server <- function(input, output, session) 

    # change 'Search words' ----
    observeEvent(input$words, 

      # handle no words (reset everything)
      if (is.null(input$words)) 
        cowords <- letters

       else 
        # update cowords (choices for selectizeInput)
        cowords <- unique(c(input$words, sample(letters, 5)))
      

      # update UI
      print('updating')
      updateSelectizeInput(session, 'words', choices = cowords, selected = input$words, server = TRUE)

    , ignoreNULL = FALSE)
  
  runGadget(shinyApp(ui, server), viewer = browserViewer())


run_ui()

我怎样才能做到这一点?

【问题讨论】:

【参考方案1】:

如果你想坚持server = TRUE,这可能不是一个小问题。

一种可能的解决方法是 debounce 您正在观察的输入,然后检查并仅在有变化时更新。这可能如下所示 - 我添加了一些 print 语句,以便您可以更好地了解正在发生的事情。

library(shiny)

run_ui <- function() 

  ui <- selectizeInput('words', 'Search words:', choices = NULL, selected = NULL, multiple = TRUE, options = NULL)

  server <- function(input, output, session) 

    val <- "a"
    pasteCollPlus <- function(...) 
      paste(..., collapse = "+")
    

    wordSelect <- debounce(reactive(input$words), millis = 50)

    # change 'Search words' ----
    observeEvent(wordSelect(), 

      # handle no words (reset everything)
      if (is.null(input$words)) 
        cowords <- letters
       else 
        # update cowords (choices for selectizeInput)
        cowords <- unique(c(input$words, sample(letters, 5)))
      

      if (isTRUE(pasteCollPlus(val) == pasteCollPlus(input$words))) 
        print(paste("No update - val is", pasteCollPlus(val)))
       else 
        # update UI
        print(paste("updating selection to", pasteCollPlus(input$words)))
        print(paste("val is", pasteCollPlus(val)))
        val <<- input$words
        updateSelectizeInput(session, 'words', choices = cowords, selected = input$words, server = TRUE)
      

    , ignoreNULL = FALSE)

  
  runGadget(shinyApp(ui, server), viewer = browserViewer())


run_ui()

编辑

另一种解决方法是明确处理弹跳模式,以阻止它。这可能更不优雅,但对于更多涉及/复杂的案例(应用程序)可能更健壮。一个例子如下:

library(shiny)
run_ui <- function() 

  ui <- selectizeInput('words', 'Search words:', choices = NULL, selected = NULL, multiple = TRUE, options = NULL)

  server <- function(input, output, session) 

    val <- "a"
    newVal <- NULL
    pasteCollPlus <- function(...) 
      paste(..., collapse = "+")
    

    # change 'Search words' ----
    observeEvent(input$words, 

      # handle no words (reset everything)
      if (is.null(input$words)) 
        cowords <- letters
       else 
        # update cowords (choices for selectizeInput)
        cowords <- unique(c(input$words, sample(letters, 5)))
      

      if (isTRUE(pasteCollPlus(val) == pasteCollPlus(input$words))) 
        print(paste("No update - val is", pasteCollPlus(val)))
        val <<- newVal
       else 
        # update UI
        print(paste("updating selection to", pasteCollPlus(input$words)))
        print(paste("val is", pasteCollPlus(val)))
        print(paste("newVal is", pasteCollPlus(newVal)))

        val <<- NULL
        newVal <<- input$words

        updateSelectizeInput(session, 'words', choices = cowords, selected = input$words, server = TRUE)
      

    , ignoreNULL = FALSE)

  
  runGadget(shinyApp(ui, server), viewer = browserViewer())


run_ui()

【讨论】:

谢谢罗兰。这可行,但似乎有点脆弱 - 实际应用程序的更新计算时间会有所不同。我必须为debounce 制作millis = 3000,我猜如果在功能较弱的计算机上运行它必须更高? 长时间的去抖动也会不必要地减慢快速更新。 是的,我同意,它很可能不适合您的实际生产设置。我编辑了答案给你一个替代方案。希望有帮助 感谢 Roland,尽管我不遵循逻辑,但我已经设法让编辑后的解决方案正常工作。【参考方案2】:

需要使用服务器端选择吗?如果没有,那么您的代码只需删除该部分即可正常工作。

library(shiny)
run_ui <- function() 

  ui <- selectizeInput('words', 'Search words:', choices = NULL, selected = NULL, multiple = TRUE, options = NULL)

  server <- function(input, output, session) 

    # change 'Search words' ----
    observeEvent(input$words, 

      # handle no words (reset everything)
      if (is.null(input$words)) 
        cowords <- letters

       else 
        # update cowords (choices for selectizeInput)
        cowords <- unique(c(input$words, sample(letters, 5)))
      

      # update UI
      print('updating')
      updateSelectizeInput(session, 'words', choices = cowords, selected = input$words)

    , ignoreNULL = FALSE)
  
  runGadget(shinyApp(ui, server), viewer = browserViewer())


run_ui()

【讨论】:

是的——在真正的应用程序中,cowords 非常大。【参考方案3】:

以下解决方案只是通过 renderUI 更新整个对象并重新绘制它,而不是通过 updateSelectizeInput() 传回更新。这确实允许在服务器端完全管理选择。缺点是它会随着每个更改事件触发,这意味着 multiple=TRUE 没有实际意义,因为每次更改都会重绘对象。如果倍数很关键,我认为updateSelectizeInput() 方法或任何其他更新onChange 的解决方案都会遇到同样的问题。要允许多个选择,事件需要移动到 onBlur 或 mouseout 事件。否则,事件触发器不知道用户是否打算只选择一个选项并触发;或者等待用户做出多项选择后再触发。但是,从用户的角度来看,blur 或 mouseout 可能会使其行为异常。强制更新操作的按钮将解决此问题。根据第一次选择保持更新,解决方法如下:

library(shiny)
run_ui <- function() 

  ui <- uiOutput(outputId="select_words")      
  server <- function(input, output, session) 

    # change 'Search words' ----
    output$select_words <- renderUI(    
      cowords <- letters
      if (!is.null(input$words)) cowords <- unique(c(input$words, sample(letters, 5)))
      print(paste("Updating words: ",paste0(cowords,collapse=",")))

      return (tagList(selectizeInput('words', 'Search words:', choices = cowords, selected = input$words, multiple = TRUE, options = NULL)))
    )
  
  runGadget(shinyApp(ui, server), viewer = browserViewer())


run_ui()

【讨论】:

谢谢索伦。尝试实现这一点并没有像updateSelectizeInput 那样扩展。为了使其更具可比性,cowords &lt;- sample(letters, 348000, replace=TRUE) 这意味着一个包含 348,000 个项目的下拉列表——无论是使用 updateSelectizeInput() 还是任何其他解决方案,都存在从服务器到客户端的数据问题?我认为,缩放比技术发送更多的数据。同样,您的下拉列表是否会向用户展示 300,000 多个项目;使用起来似乎很麻烦。如果您从这个庞大的列表中抽取一个子集,那么子集服务器端应该非常快。 此外,在使用letters 的示例中,请注意html 下拉菜单将仅显示唯一项目。因此,将 348,000 个冗余项传递到下拉菜单将仅生成 26 个唯一字母的列表。我知道这只是一个示例,但是如果您的应用程序列表中有多余的项目,那么这个问题也会在那里出现。 是的,这 348,000 件商品都是独一无二的。我不确定selectizeInput 是否显示所有项目,但是将它们全部设置为choices 会使它们都可搜索。我尝试实施您的解决方案,因为它比 RolandASc 的答案(呈现没有问题)更复杂,但搜索输入没有在几分钟内呈现。我只是给出了字母示例来重现我所经历的滞后。

以上是关于Shiny:根据选择更新 selectizeInput 选择的主要内容,如果未能解决你的问题,请参考以下文章

Shiny - 更新选择输入的输入文本

Shiny:根据 selectInput 选择动态分配 var 名称

R SHINY:根据 selectInput/numericInput 选项清除/更新 mainPanel

根据数据表行选择更新小部件值

SelectInput 选项不会根据在 Shiny 中选择的 csv 文件动态填充

更新 R Shiny 中的 DT 列过滤器选择