使用 server = T 防止闪亮的 updateSelectizeInput 闪烁

Prevent flickering of shiny updateSelectizeInput with server = T

我有一个闪亮的应用程序,在 selectizeInput 中有两种选择,一种是长的,一种是短的。如果用户只想看到短的,他们可以点击一个复选框,选择会相应地改变。此外,如果用户看到长选项,select 一个选项也在短列表中,然后单击复选框,selected 选项应保持 selected。反之亦然。到目前为止,一切都在以下应用程序中运行,该应用程序使用 reactiveValuesupdateSelectizeInput

library("shiny")

choicesONE <- c("a","b","c","d","e")

choicesTWO <- c("a","c","e")

ui <- shinyUI(fluidPage(

  sidebarLayout(

    sidebarPanel(

      selectizeInput(inputId="topic",
                     label = ("Topic"),
                     choices=NULL,
                     multiple = T,
                     options=list(maxItems = 1,
                                  placeholder="Please choose...")),

      checkboxInput("sub", "Show only subchoices", value = FALSE, width = NULL)

    ),

    mainPanel(

    )

  )

))

server <- function(input, output, session) {

  #------- Initialize the Memory ----------

  choice <- reactiveValues(selection = NULL) 

  #------ Whenever the inputs are changed, it only modifies the memory----

  observeEvent(input$topic,{

    choice$selection <- input$topic

  })

  #------ Update UI element using the values stored in memory ------

  observe({

    if(input$sub==T) {

      updateSelectizeInput(session,
                           server = T,
                           'topic',
                           choices = choicesTWO,
                           selected = choice$selection)

    } else {

      updateSelectizeInput(session,
                           server = T,
                           'topic',
                           choices = choicesONE,
                           selected = choice$selection)


    }


  })



}

shinyApp(ui = ui, server = server)

在我的真实应用程序中,选项列表包含数千个选项,如果不使用 server = T,应用程序的速度会大大降低。

但是如果我将服务器设置为 true,每次单击复选框后 selectize 字段都会清空然后再次填充,因此 selectize 字段会闪烁。这是非常没有吸引力的,尤其是用户不友好的。

有谁知道如何防止闪烁的同时保持server = T

只需在您的观察器中使用 isolate

observe({
  if(input$sub==T) {
    isolate(
      updateSelectizeInput(
        session,
        server = T,
        'topic',
        choices = choicesTWO,
        selected = choice$selection
      )
    )
  } else {
    isolate(
      updateSelectizeInput(
        session,
        server = T,
        'topic',
        choices = choicesONE,
        selected = choice$selection
      )
    )
  }
})

我不确定您是否可以在不进入 selectize.js 的情况下获得完全满意的东西。它可能有点 hacky,但如果你只关注用户体验,它就可以完成工作:

choicesONE <- as.character(sample(1:1000000, size = 1000))
choicesTWO <- sample(choicesONE, size = 20)

...

observe({

    if (input$sub) {
        input_choices <- choicesTWO
    } else {
        input_choices <- choicesONE
    }

    input_placeholder <- isolate(input$topic)
    if (!(is.null(input_placeholder) || input_placeholder %in% choicesTWO)) {
        input_placeholder <- "Please choose..."
    }

    isolate(
        updateSelectizeInput(
            session,
            server = T,
            'topic',
            choices = input_choices,
            selected = choice$selection,
            options = list(placeholder=input_placeholder))
    )

})

为了使它更加无缝,您可以使用一点 CSS。

由于默认显示的是较长的选项列表,考虑到较短的列表要短得多,我认为呈现两个列表不会增加太多负担。

这里的技巧是呈现两个列表,但根据复选框的值隐藏其中一个。这样,我们只需要调用一次 renderUI 就可以在服务器端生成 DOM 并且我们已经可以传入选项了。 (我从@kluu的回答中借用了这个例子,谢谢!)

这样,我们可以更新 selectizeInput 的选定选项,而不是更新 'choices' 参数。而且 reactiveVal selectedValue 总是有正确的选择。

library("shiny")

ui <- shinyUI(fluidPage(

  sidebarLayout(
    sidebarPanel(
      uiOutput("selectInput"),
      checkboxInput("sub", "Show only subchoices", value = FALSE, width = NULL),
      textOutput("debug")
    ),
    mainPanel()
  )

))

server <- function(input, output, session) {
  choicesONE <- as.character(sample(1:1000000, size = 1000))
  choicesTWO <- sample(choicesONE, size = 20)

  output$selectInput <- renderUI({
    tagList(
      conditionalPanel(
        "!input['sub']",
        selectizeInput(
          "longTopic",
          "Topic",
          choices = choicesONE,
          multiple = FALSE,
          options = list(placeholder = "Please choose...")
        )
      ),

      conditionalPanel(
        "input['sub']",
        selectizeInput(
          "shortTopic",
          "Topic",
          choices = choicesTWO,
          multiple = FALSE,
          options = list(placeholder = "Please choose...")
        )
      )
    )
  })

  selectedValue <- reactiveVal(NULL)

  observe({
    if (input$sub) {
      selectedValue(input$shortTopic)
    } else {
      selectedValue(input$longTopic)
    }
  })

  observeEvent(input$sub, {
    id <- ifelse(input$sub, "shortTopic", "longTopic")
    updateSelectizeInput(session, id,  selected = selectedValue())
  })

  output$debug <- renderText({
    selectedValue()
  })
}

shinyApp(ui = ui, server = server)