Skip to contents

Create a reactive value that can be updated imperatively (like a shiny::reactiveVal()) but whose state is ultimately governed by a reactive validation expression. This ensures that its value is always consistent with its reactive dependencies before any downstream dependents are re-evaluated.

Usage

validated_reactive_val(
  validation_expr,
  value = NULL,
  default = NULL,
  label = NULL,
  env = rlang::caller_env()
)

# S3 method for class 'vrv'
x$name

Arguments

validation_expr

(expression) A reactive expression that defines the authoritative state of this validated_reactive_value, This expression can access the validated_reactive_value's own current value via the .vrv() pronoun to reconcile it with upstream dependencies.

value

(various) The initial value. This value will be coerced via the validation expression when accessed. If this value is reactive, an observer with priority = Inf will be created to attempt to keep the validated value in sync with that reactive.

default

(various, including expression or reactive expression) A value to use when the current value is not valid according to the defined rules. Defaults to NULL.

label

(length-1 character or NULL) An optional label for the validated_reactive_value, used for debugging.

env

(environment) The environment in which to evaluate the validation_expr.

x

(vrv) A vrv object.

name

(length-1 character) The name of the helper function to access. Expects one of value, error, or is_default. Other values return NULL.

Value

A vrv object, which is a function with a custom class. Call it with no arguments to (reactively) read the validated value. Call it with a single argument to imperatively set the value; it will be automatically validated on the next read. You can also access the most recent validation error with my_vrv$error() and check if the current value is the default with my_vrv$is_default().

Examples

if (FALSE) { # interactive()

  library(shiny)

  ui <- fluidPage( selectInput("level", "Level", choices = c("A", "B")),
  uiOutput("group_ui"), textOutput("current_group") )

  server <- function(input, output, session) {
    # `group_val` depends on `input$level` and can also be set by `input$group`.
    group_val <- validated_reactive_val(
      value = "A1",
      validation_expr = {
        # If the current value is not valid for the new level, reset it.
        valid_groups <- if (input$level == "A") {
          c("A1", "A2")
        } else {
          c("B1", "B2")
        }
        if (.vrv() %in% valid_groups) {
          .vrv()
        } else {
          valid_groups[[1]]
        }
      }
    )

    # When the user changes the group input, imperatively update the vrv.
    observeEvent(input$group, {
      group_val(input$group)
    })

    # The UI for the group dropdown is dynamic.
    output$group_ui <- renderUI({
      choices <- if (input$level == "A") c("A1", "A2") else c("B1", "B2")
      selectInput("group", "Group", choices = choices, selected = group_val())
    })

    output$current_group <- renderText({
      paste("Current Validated Group:", group_val())
    })
  }

  shinyApp(ui, server)
}