首页 > 解决方案 > 如何管理/调用具有未满足“验证(需要())”条件的闪亮“反应()”表达式

问题描述

应该如何检查反应式表达式validate(need())中的条件是否满足?

当该表达式不满足条件时尝试访问反应式表达式会导致静默错误并停止函数/观察者存储初始调用的任何内容。虽然从 UI 的角度来看这很有意义,但如何从功能的角度来处理它并防止过早停止呢?

下面是一个复制此行为的示例(基于真正的 Shiny Validate 教程):直到您在 中选择一个值selectInput(),您将不会看到控制台消息“只有在满足 data() 验证/需要时才会打印此消息。” 单击按钮时,因为observeEvent()已停止。与此同时,你可能仍然渴望做一些事情,比如写一个空文件或此时触发一些事情等。

require(shiny)

ui <- fluidPage(
  sidebarLayout(
    sidebarPanel(
      selectInput("data", label = "Data set",
                  choices = c("", "mtcars", "faithful", "iris"))
    ),
    
    mainPanel(
      actionButton(inputId = "actbtn", label = "Print in console reactive value")
    )
  )
)

server <- function(input, output) {
  
  data <- reactive({
    validate(
      need(input$data != "", "Please select a data set"),
      need(input$data %in% c("mtcars", "faithful", "iris"),
           "Unrecognized data set"),
      need(input$data, "Input is an empty string"),
      need(!is.null(input$data),
           "Input is not an empty string, it is NULL")
    )
    get(input$data, 'package:datasets')
  })
  
  observeEvent(input$actbtn, {
    print(data())
    print("This message will only be printed when data() validate/need are fulfilled.")
  })
}

shinyApp(ui, server)

我试图在实际调用它之前检查反应表达式的“状态”,但目前没有成功。我当然可以调整我的应用程序(例如,通过我自己进行类似验证或只是在调用反应式表达式之前重新执行条件),但我非常有信心有一种更智能的方法来处理这种情况。

标签: rshiny

解决方案


使用reqor validate/need时,如果不完全有效,反应链就会被打断。这意味着任何依赖于反应块的东西也不会触发(由于data块)。我不知道“窥视”反应块验证的方法,因为这将涉及评估表达式并捕获特定于闪亮的信号。

通常,在闪亮的应用程序中,actbtn观察者不会因data未验证而触发就足够了。但是,如果您需要在一个表达式中知道另一个表达式是否可以触发,您可以设置第三个反应块,它总是返回一个已知元素(即,其中没有validate)。

library(shiny) # don't use require without checking its return value
               # https://stackoverflow.com/a/51263513/3358272

ui <- fluidPage(
  sidebarLayout(
    sidebarPanel(
      selectInput("data", label = "Data set",
                  choices = c("", "mtcars", "faithful", "iris"))
    ),
    
    mainPanel(
      actionButton(inputId = "actbtn", label = "Print in console reactive value")
    )
  )
)

server <- function(input, output) {
  
  isgood <- reactive({
    if (is.null(input$data) || !nzchar(input$data)) {
      "Please select a data set"
    } else if (!input$data %in% c("mtcars", "faithful", "iris")) {
      "Unrecognized data set"
    } else character(0)
  })

  data <- reactive({
    validate(need(!length(isgood()), isgood()))
    get(input$data, 'package:datasets')
  })
  
  observeEvent(input$actbtn, {
    validate(need(!length(isgood()), isgood()))
    print("This message will only be printed when data() validate/need are fulfilled.")
  })
}

shinyApp(ui, server)

另一种方法是禁用按钮(无法单击),直到data()有效。这可以通过 来完成shinyjs,这也需要对ui组件进行更改。

library(shiny)
library(shinyjs)

ui <- fluidPage(
  shinyjs::useShinyjs(),                    # <-- add this
  sidebarLayout(
    sidebarPanel(
      selectInput("data", label = "Data set",
                  choices = c("", "mtcars", "faithful", "iris"))
    ),

    mainPanel(
      actionButton(inputId = "actbtn", label = "Print in console reactive value")
    )
  )
)

server <- function(input, output) {

  observeEvent(TRUE, {
    message("quux")
    shinyjs::disable("actbtn")
  }, once = TRUE)

  data <- reactive({
    validate(
      need(input$data != "", "Please select a data set"),
      need(input$data %in% c("mtcars", "faithful", "iris"),
           "Unrecognized data set"),
      need(input$data, "Input is an empty string"),
      need(!is.null(input$data),
           "Input is not an empty string, it is NULL")
    )
    get(input$data, 'package:datasets')
  })

  observeEvent(data(), {
    shinyjs::toggleState("actbtn", condition = !is.null(data()))
  })

  observeEvent(input$actbtn, {
    print("This message will only be printed when data() validate/need are fulfilled.")
  })
}

shinyApp(ui, server)

由于您在 UI 中可用的选项,toggleState观察者只会切换一次禁用状态,之后它应该始终有效。因此,有一些方法可以减少它。例如,一次可以像我添加once=TRUE的第一个一样observeEvent添加,在这种情况下,一旦观察者触发一次,它就会被删除并且永远不会再次触发。在您的实际使用中,如果您认为这data()可能会切换回不可用状态(例如,0 行),那么您可能希望保留默认值并在验证中once=FALSE添加更多逻辑。nrow(data())data


推荐阅读