如果 chooserInput 的右侧为空,则使用 shinyBS 禁用 actionButton 时出现 R Shiny 错误

R Shiny error when using shinyBS to disable actionButton if right-side of chooserInput is empty

这是我关于 Whosebug 的第一个问题,我已经使用 R 3 个月了。我有很多要学习的!任何帮助深表感谢。预先感谢您的宝贵时间。

我想要发生的事情: 用户从下拉框中选择一个类别(动物或食物)并单击下一步。适当的 ui 组件将呈现并显示。然后,只要 Chooser Input 组件的右侧框为空,Next 按钮就应该被禁用(变灰)。只有当用户在右侧框中至少有一个选择时,才应启用“下一步”按钮,用户才可以单击它。

问题: Chooser Input 组件呈现后,右侧框为空,但 Next 未禁用。这是错误:

Warning in run(timeoutMs) :
Unhandled error in observer: argument is of length zero
observeEvent(input$widget2)

下面是演示 ui.R 和 sever.R 来重现我的问题。 (不过,我会将解决方案实施到更大、更复杂的 GUI 中。)代码使用 shinyBS,因此您首先需要安装包并加载库。该代码还使用 chooserInput,它需要 uires 两个文件:chooser.Rwww/chooser-binding.js.有关详细信息,请参阅以下 link: http://shiny.rstudio.com/gallery/custom-input-control.html

ui

### The following libraries need to be loaded BEFORE runApp()
### library(shiny)
### library(shinyBS)

source("chooser.R") # Used for Custom Input Control UI component (chooserInput)
# chooser.R is saved in the same location as the ui.R and server.R files
# chooserInput also requires chooser-binding JScript script file, which should be located within "www" folder


shinyUI(navbarPage("navbarPage Title",
       tabPanel("tabPanel Title", titlePanel("titlePanel Title"),
            fluidPage(

              #### NEW ROW #####################################################################################################  
              fluidRow(wellPanel(

                #  Instructions for initial screen
                conditionalPanel(condition = "input.ButtonNext == 0", tags$b("Step 1: Choose category and click 'Next'")),

                # Instructions for 'Foods'
                conditionalPanel(condition = "input.ButtonNext == 1 && input.widget1 == 'Foods'", tags$b("Step 2: Move Food(s) of interest to the right box and click 'Next'")),

                # Instructions for 'Animals'
                conditionalPanel(condition = "input.ButtonNext == 1 && input.widget1 == 'Animals'", tags$b("Step 2: Move Animals(s) of interest to the right box and click 'Next'"))

              )),

              #### NEW ROW #####################################################################################################  
              fluidRow( 

                # Drop down box for first selection
                conditionalPanel(
                  condition = "input.ButtonNext == 0", 
                  selectInput("widget1", label = "",
                                choices = c("Foods", 
                                "Animals"))),

                # This outputs the dynamic UI components based on first selection
                uiOutput("ui1")

              ), 

              #### NEW ROW #####################################################################################################  
              fluidRow(tags$hr()), # Horizontal line separating input UI from "Next" button

              #### NEW ROW #####################################################################################################  
              fluidRow(
                column(1, offset=10,
                   # UI component for 'Next' button
                   conditionalPanel(condition = "input.ButtonNext < 2", bsButton("ButtonNext", "Next"))
                ),

                column(1,
                   HTML("<a class='btn' href='/'>Restart</a>")
                )  
              )   


            ) # End of fluidPage 
       ) # End of tabPanel

)) # End of navbarPage and ShinyUI

服务器

shinyServer(function(input, output, session) {


      # Widget to display when number of clicks of "Next" button (ButtonNext) =  1    
      output$ui1 <- renderUI({
        if(input$ButtonNext[1]==1) {

          print("I am in renderUI") # Used to help debug

          # Depending on the initial selection, generate a different UI component
          switch(input$widget1,
                 "Foods" = chooserInput("widget2", "Available frobs", "Selected frobs", leftChoices=c("Apple", "Cheese", "Carrot"), rightChoices=c(), size = 5, multiple = TRUE),
                 "Animals" = chooserInput("widget2", "Available frobs", "Selected frobs", leftChoices=c("Lion", "Tiger", "Bear", "Wolverine"), rightChoices=c(), size = 5, multiple = TRUE)
          )

        } 
      }) # End of renderUI


      # Disable "Next" button when right side of multi select input is empty
      observeEvent(input$widget2, ({

        widget2_right <- input$widget2[[2]]
        print(widget2_right) # Used to help debug

        if(widget2_right == character(0)) {
          updateButton(session, "ButtonNext", disabled = TRUE)
        } else {
          updateButton(session, "ButtonNext", disabled = FALSE)
        }

      })) # End of observeEvent


}) # End of shinyServer

A similar question (link below) mentioned using Priorities and Resume/Suspend 但没有提供示例。如果这是解决我的问题的有效方法,请提供一些代码。

R shiny Observe running Before loading of UI and this causes Null parameters

额外注意:所提供的代码是从我为用户开发的更大的 GUI 重新创建的一个小演示,以便用户进行一系列选择,单击 'Next'在每个选择之间。根据他们在每一步所做的选择,新的选择会从一个 csv 文件中生成。因此,呈现和显示的 ui 组件取决于用户单击 'Next' 的次数以及他们之前所做的选择。最后,选择用于对大型数据集进行排序,以便用户可以仅绘制他们感兴趣的数据。双重条件是我使用 conditionalPanel 和 actionButton 的 VALUE 来呈现和显示当前 ui 用户需要的组件。我的代码有效(除了上面的问题 - 哈!)。但是,我读到使用 actionButton 的 VALUE 是糟糕的编码习惯。如果对处理双重条件的另一种方法有任何建议,请告诉我。

在server.R中,我替换了

if(widget2_right == character(0))

if(length(widget2_right)==0)

现在程序可以正常运行了。

当右框为空时,widget2_right = character(0)。我了解到将向量与 character(0) 进行比较会得到 logical(0),而不是 TRUEFALSE。然而,length(character(0)) = 0。因此,当右侧框中没有任何选择时,if(length(widget2_right)==0) 将是 TRUE