R 闪亮 - updateSelectizeInput 在与 actionButton

时间:2021-05-28 13:02:54

标签: r shiny

问题updateSelectizeInput 中的 observeEvent 调用会更改浏览器中显示的值,但不会更改我可以使用 input$< 从代码访问的值/p>

背景:在我的 Shiny 应用中,我希望输入具有以下属性:

  1. 用户可以输入并从服务器端选项列表中进行选择
  2. 一个 URL 查询字符串也可以控制它
  3. 空白表示所有选项
  4. actionButton 允许用户延迟昂贵的计算,直到做出所有想要的选择

我目前有一个 observeEvent 监视查询字符串,当它看到一个时,它调用 updateSelectizeInput,但是,在它这样做之后,输入不变。

示例

library(shiny)

possibleLetters = LETTERS[1:10]

ui <- fluidPage(
   
   sidebarLayout(
      sidebarPanel(
        selectizeInput(inputId='letters', 
                       label='Your letters:', 
                       choices=NULL,
                       selected=NULL, 
                       multiple=T, 
                       width='100%'),
        actionButton("recompute",
                     "Recompute now")
      ),
      
      mainPanel(
        h3("Letters: "),
        textOutput('lettersDisplay'),
        h3("Indices of letters: "),
        textOutput('lettersIndicesDisplay')
      )
   )
)

server <- function(input, output, session) {
   
  updateSelectizeInput(inputId='letters',
                       choices=c('',possibleLetters),
                       server=T)

  userLetters = eventReactive(input$recompute, ignoreNULL=FALSE, {
    if (length(input$letters) == 0) {
      return (possibleLetters)
    } else (
      return (input$letters)
    )
  })
  
  userLetterIndices = reactive({
    return(match(userLetters(),LETTERS))
  })
  
  queryStringObserver = observeEvent(session$clientData$url_search, {
    query <- parseQueryString(session$clientData$url_search)
    if (!is.null(query$letters)) {
      cat(file=stderr(), paste0('observeEvent running - user specified param is: ',query$letters,'\n'))
      updateSelectizeInput(session, 
                           inputId="letters",
                           choices = possibleLetters,
                           selected = query$letters,
                           server=T)
      cat(file=stderr(), paste0('observeEvent running - ran updateSelectizeInput, input$letters is now: ',input$letters,'\n'))
    }
  })
  
  
  output$lettersDisplay = renderText({
    return(paste(userLetters(),collapse=' '))
  })
  
  output$lettersIndicesDisplay = renderText({
    return(paste(userLetterIndices(), collapse=' '))
  })

}

shinyApp(ui = ui, server = server, options = list(port=1111))

重现问题的步骤:运行应用程序,然后导航到 http://localhost:1111/?letters=A

您会发现浏览器窗口的 selectize 字段中确实填充了“A”,但是,input 值尚未更新。在您的控制台中,您将看到:

observeEvent running - user specified param is: A
observeEvent running - ran updateSelectizeInput, input$letters is now: 

这样,查询字符串已经被正确解析,updateSelectizeInput已经被调用,但是当之后访问input$letters时,它的值没有改变。

我怀疑这与我对反应图或其他东西的理解中的一些基本缺陷有关,但在仔细研究Mastering Shiny后,我仍然无法弄清楚为什么这个调用没有任何作用。

2 个答案:

答案 0 :(得分:1)

input$letters 的值更新,只是在您尝试打印它时还没有更新。我不确定 Shiny 如何或是否批量处理消息,但您的 observeEvent 最终会触发一条消息发送到客户端以更新输入,然后必须通知服务器已更新。至少,我假设它会完成当前观察者代码的执行,但通过一些修补看起来 Shiny 可能会在向客户端发送消息之前执行所有必要的反应代码。

虽然 input$letters 的值不会使用您的给定代码打印任何内容,但如果我单击重新计算,它确实会按预期更新文本。基本上,这里或多或少是我认为与您的代码发生的对话:

Client: Yo, server. The user added a query parameter: letters=A.

Server: Hmmm, ok I will just run this observeEvent code. Oh, the developer wants to know the current value of `input$letters`. Client, can you help a server out with that input value.

Client: No problem, friend-o. The current selected value is NULL.

Server: Well, let me just cat this NULL to the stderr. 

Server (~1 ms later): Yo, client. I finished running the observeEvent code and you should really update that selectize input. It would make everyone happy.

Client: Can do.

Client (~2 ms later): Whoa, the select input updated. I gots to tell server about this jawn. Hey server, input$letters just changed to `A`. Just FYI.

Server: Hmm, good to know. Nothing for me to do about that.

当服务器打印 inputletters 时,该值仍然为 NULL,因为它还没有告诉客户端更新它。实际上,我不确定服务器是否轮询客户端以获取该值,或者是否从它自己的当前值列表中查找它,但无论哪种方式,当它转到该值时它仍然没有更新。

将您的 cat 语句移至单独的 observe 语句,上面的对话将更改为

Client (~2 ms later): Whoa, the select input updated. I gots to tell server about this jawn. Hey server, input$letters just changed to `A`. Just FYI.

Server: WHAT?!!! OMG! I MUST TELL STDERR ABOUT THIS!!

我认为您的代码本身实际上没有任何问题。抱歉,服务器和客户端的所有个性都发生了变化,但希望这会有所帮助。

答案 1 :(得分:0)

要实现应用程序的预期行为,立即使用给定的查询字符串显示结果(无需等待用户立即按重新计算),需要进行以下更改:

  1. 删除原来的观察者,而是只从 userLetters eventReactive 内部调用 updateSelectizeInput
  2. 仅在启动时填写查询字符串中的值 (if(!input$recompute))
  3. 要使查询字符串与用户随后在应用中更改的内容保持同步,请添加新的观察者 update_qs

代码:


library(shiny)

possibleLetters = LETTERS[1:10]

ui <- fluidPage(
   
   sidebarLayout(
      sidebarPanel(
        selectizeInput(inputId='letters', 
                       label='Your letters:', 
                       choices=NULL,
                       selected=NULL, 
                       multiple=T, 
                       width='100%'),
        actionButton("recompute",
                     "Recompute now")
      ),
      
      mainPanel(
        h3("Letters: "),
        textOutput('lettersDisplay'),
        h3("Indices of letters: "),
        textOutput('lettersIndicesDisplay')
      )
   )
)

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

  userLetters = eventReactive(input$recompute, ignoreNULL=FALSE, {
    query <- parseQueryString(session$clientData$url_search)
    if (!input$recompute & !is.null(query$letters)) {
      selectedLetters = strsplit(query$letters,';')[[1]]
    } else if (is.null(input$letters)) {
      selectedLetters = character(0)
    } else (
      selectedLetters = input$letters
    )
    updateSelectizeInput(session, 
                         inputId="letters",
                         choices = c('',possibleLetters),
                         selected = selectedLetters,
                         server=T)
    if (length(selectedLetters)==0) {
      return (possibleLetters)
    } else {
      return (selectedLetters)
    }
  })
  
  update_qs = observeEvent(input$recompute, {
    if (!identical(userLetters(),possibleLetters)) {
      new_qs = paste0('?letters=',paste0(userLetters(),collapse=';'))
    } else {
      new_qs = '?'
    }
    updateQueryString(new_qs, mode='push')
  })
  
  userLetterIndices = reactive({
    return(match(userLetters(),LETTERS))
  })

  
  output$lettersDisplay = renderText({
    return(paste(userLetters(),collapse=' '))
  })
  
  output$lettersIndicesDisplay = renderText({
    return(paste(userLetterIndices(), collapse=' '))
  })

}

# Run the application 
shinyApp(ui = ui, server = server, options = list(port=1111))