From 196b220faf7d41d06f3d0e50746d88a653f68dfd Mon Sep 17 00:00:00 2001 From: Winston Chang Date: Tue, 8 Dec 2020 12:18:37 -0600 Subject: [PATCH] All session parameters from the update* functions now default to `getDefaultReactiveDomain()` (#3195) Co-authored-by: colin --- NEWS.md | 4 +- R/shiny.R | 14 ++++++ R/update-input.R | 79 ++++++++++++++++++++++++--------- man-roxygen/update-input.R | 2 +- man/updateActionButton.Rd | 16 +++++-- man/updateCheckboxGroupInput.Rd | 4 +- man/updateCheckboxInput.Rd | 9 +++- man/updateDateInput.Rd | 4 +- man/updateDateRangeInput.Rd | 4 +- man/updateNumericInput.Rd | 4 +- man/updateRadioButtons.Rd | 4 +- man/updateSelectInput.Rd | 10 ++--- man/updateSliderInput.Rd | 4 +- man/updateTabsetPanel.Rd | 20 +++++++-- man/updateTextAreaInput.Rd | 4 +- man/updateTextInput.Rd | 4 +- 16 files changed, 133 insertions(+), 53 deletions(-) diff --git a/NEWS.md b/NEWS.md index 98ea404b74..c7a50aee86 100644 --- a/NEWS.md +++ b/NEWS.md @@ -64,10 +64,12 @@ shiny 1.5.0.9000 * Closed #3140: Added support for `...` argument in `icon()`. (#3143) -* Improved error messages when reading reactive values outside of a reactive domain (e.g., `reactiveVal()()`). (#3007) +* Closed #629: All `update*` functions now have a default value for `session`, and issue an informative warning if it is missing. (#3195) ### Bug fixes +* Improved error messages when reading reactive values outside of a reactive domain (e.g., `reactiveVal()()`). (#3007) + * Fixed #1942: Calling `runApp("app.R")` no longer ignores options passed into `shinyApp()`. This makes it possible for Shiny apps to specify what port/host should be used by default. (#2969) * Fixed #3033: When a `DiskCache` was created with both `max_n` and `max_size`, too many items could get pruned when `prune()` was called. (#3034) diff --git a/R/shiny.R b/R/shiny.R index 83aadd297b..59e179c94d 100644 --- a/R/shiny.R +++ b/R/shiny.R @@ -2532,3 +2532,17 @@ markdown <- function(mds, extensions = TRUE, .noWS = NULL, ...) { html <- rlang::exec(commonmark::markdown_html, glue::trim(mds), extensions = extensions, ...) htmltools::HTML(html, .noWS = .noWS) } + + +# Check that an object is a ShinySession object, and give an informative error. +# The default label is the caller function's name. +validate_session_object <- function(session, label = as.character(sys.call(sys.parent())[[1]])) { + if (missing(session) || !inherits(session, c("ShinySession", "MockShinySession"))) { + stop(call. = FALSE, + sprintf( + "`session` must be a 'ShinySession' object. Did you forget to pass `session` to `%s()`?", + label + ) + ) + } +} diff --git a/R/update-input.R b/R/update-input.R index fade9ea9b2..7b01db0a9e 100644 --- a/R/update-input.R +++ b/R/update-input.R @@ -34,7 +34,9 @@ #' shinyApp(ui, server) #' } #' @export -updateTextInput <- function(session, inputId, label = NULL, value = NULL, placeholder = NULL) { +updateTextInput <- function(session = getDefaultReactiveDomain(), inputId, label = NULL, value = NULL, placeholder = NULL) { + validate_session_object(session) + message <- dropNulls(list(label=label, value=value, placeholder=placeholder)) session$sendInputMessage(inputId, message) } @@ -106,7 +108,9 @@ updateTextAreaInput <- updateTextInput #' shinyApp(ui, server) #' } #' @export -updateCheckboxInput <- function(session, inputId, label = NULL, value = NULL) { +updateCheckboxInput <- function(session = getDefaultReactiveDomain(), inputId, label = NULL, value = NULL) { + validate_session_object(session) + message <- dropNulls(list(label=label, value=value)) session$sendInputMessage(inputId, message) } @@ -165,7 +169,9 @@ updateCheckboxInput <- function(session, inputId, label = NULL, value = NULL) { #' } #' @rdname updateActionButton #' @export -updateActionButton <- function(session, inputId, label = NULL, icon = NULL) { +updateActionButton <- function(session = getDefaultReactiveDomain(), inputId, label = NULL, icon = NULL) { + validate_session_object(session) + if (!is.null(icon)) icon <- as.character(validateIcon(icon)) message <- dropNulls(list(label=label, icon=icon)) session$sendInputMessage(inputId, message) @@ -206,8 +212,10 @@ updateActionLink <- updateActionButton #' shinyApp(ui, server) #' } #' @export -updateDateInput <- function(session, inputId, label = NULL, value = NULL, - min = NULL, max = NULL) { +updateDateInput <- function(session = getDefaultReactiveDomain(), inputId, label = NULL, value = NULL, + min = NULL, max = NULL) +{ + validate_session_object(session) value <- dateYMD(value, "value") min <- dateYMD(min, "min") @@ -251,9 +259,11 @@ updateDateInput <- function(session, inputId, label = NULL, value = NULL, #' shinyApp(ui, server) #' } #' @export -updateDateRangeInput <- function(session, inputId, label = NULL, +updateDateRangeInput <- function(session = getDefaultReactiveDomain(), inputId, label = NULL, start = NULL, end = NULL, min = NULL, - max = NULL) { + max = NULL) +{ + validate_session_object(session) start <- dateYMD(start, "start") end <- dateYMD(end, "end") @@ -273,7 +283,7 @@ updateDateRangeInput <- function(session, inputId, label = NULL, #' Change the selected tab on the client #' #' @param session The `session` object passed to function given to -#' `shinyServer`. +#' `shinyServer`. Default is `getDefaultReactiveDomain()`. #' @param inputId The id of the `tabsetPanel`, `navlistPanel`, #' or `navbarPage` object. #' @inheritParams tabsetPanel @@ -309,7 +319,9 @@ updateDateRangeInput <- function(session, inputId, label = NULL, #' shinyApp(ui, server) #' } #' @export -updateTabsetPanel <- function(session, inputId, selected = NULL) { +updateTabsetPanel <- function(session = getDefaultReactiveDomain(), inputId, selected = NULL) { + validate_session_object(session) + message <- dropNulls(list(value = selected)) session$sendInputMessage(inputId, message) } @@ -357,9 +369,11 @@ updateNavlistPanel <- updateTabsetPanel #' shinyApp(ui, server) #' } #' @export -updateNumericInput <- function(session, inputId, label = NULL, value = NULL, +updateNumericInput <- function(session = getDefaultReactiveDomain(), inputId, label = NULL, value = NULL, min = NULL, max = NULL, step = NULL) { + validate_session_object(session) + message <- dropNulls(list( label = label, value = formatNoSci(value), min = formatNoSci(min), max = formatNoSci(max), step = formatNoSci(step) @@ -404,9 +418,11 @@ updateNumericInput <- function(session, inputId, label = NULL, value = NULL, #' ) #' } #' @export -updateSliderInput <- function(session, inputId, label = NULL, value = NULL, +updateSliderInput <- function(session = getDefaultReactiveDomain(), inputId, label = NULL, value = NULL, min = NULL, max = NULL, step = NULL, timeFormat = NULL, timezone = NULL) { + validate_session_object(session) + # If no min/max/value is provided, we won't know the # type, and this will return an empty string dataType <- getSliderType(min, max, value) @@ -439,6 +455,8 @@ updateSliderInput <- function(session, inputId, label = NULL, value = NULL, updateInputOptions <- function(session, inputId, label = NULL, choices = NULL, selected = NULL, inline = FALSE, type = NULL, choiceNames = NULL, choiceValues = NULL) { + validate_session_object(session) + if (is.null(type)) stop("Please specify the type ('checkbox' or 'radio')") args <- normalizeChoicesArgs(choices, choiceNames, choiceValues, mustExist = FALSE) @@ -496,9 +514,12 @@ updateInputOptions <- function(session, inputId, label = NULL, choices = NULL, #' shinyApp(ui, server) #' } #' @export -updateCheckboxGroupInput <- function(session, inputId, label = NULL, +updateCheckboxGroupInput <- function(session = getDefaultReactiveDomain(), inputId, label = NULL, choices = NULL, selected = NULL, inline = FALSE, - choiceNames = NULL, choiceValues = NULL) { + choiceNames = NULL, choiceValues = NULL) +{ + validate_session_object(session) + updateInputOptions(session, inputId, label, choices, selected, inline, "checkbox", choiceNames, choiceValues) } @@ -539,9 +560,12 @@ updateCheckboxGroupInput <- function(session, inputId, label = NULL, #' shinyApp(ui, server) #' } #' @export -updateRadioButtons <- function(session, inputId, label = NULL, choices = NULL, +updateRadioButtons <- function(session = getDefaultReactiveDomain(), inputId, label = NULL, choices = NULL, selected = NULL, inline = FALSE, - choiceNames = NULL, choiceValues = NULL) { + choiceNames = NULL, choiceValues = NULL) +{ + validate_session_object(session) + # you must select at least one radio button if (is.null(selected)) { if (!is.null(choices)) selected <- choices[[1]] @@ -591,8 +615,11 @@ updateRadioButtons <- function(session, inputId, label = NULL, choices = NULL, #' shinyApp(ui, server) #' } #' @export -updateSelectInput <- function(session, inputId, label = NULL, choices = NULL, - selected = NULL) { +updateSelectInput <- function(session = getDefaultReactiveDomain(), inputId, label = NULL, choices = NULL, + selected = NULL) +{ + validate_session_object(session) + choices <- if (!is.null(choices)) choicesWithNames(choices) if (!is.null(selected)) selected <- as.character(selected) options <- if (!is.null(choices)) selectOptions(choices, selected, inputId, FALSE) @@ -607,9 +634,12 @@ updateSelectInput <- function(session, inputId, label = NULL, choices = NULL, #' `choices` into the page at once (i.e., only use the client-side #' version of \pkg{selectize.js}) #' @export -updateSelectizeInput <- function(session, inputId, label = NULL, choices = NULL, +updateSelectizeInput <- function(session = getDefaultReactiveDomain(), inputId, label = NULL, choices = NULL, selected = NULL, options = list(), - server = FALSE) { + server = FALSE) +{ + validate_session_object(session) + if (length(options)) { res <- checkAsIs(options) cfg <- tags$script( @@ -722,12 +752,15 @@ updateSelectizeInput <- function(session, inputId, label = NULL, choices = NULL, #' @rdname updateSelectInput #' @inheritParams varSelectInput #' @export -updateVarSelectInput <- function(session, inputId, label = NULL, data = NULL, selected = NULL) { +updateVarSelectInput <- function(session = getDefaultReactiveDomain(), inputId, label = NULL, data = NULL, selected = NULL) { + validate_session_object(session) + if (is.null(data)) { choices <- NULL } else { choices <- colnames(data) } + updateSelectInput( session = session, inputId = inputId, @@ -738,7 +771,11 @@ updateVarSelectInput <- function(session, inputId, label = NULL, data = NULL, se } #' @rdname updateSelectInput #' @export -updateVarSelectizeInput <- function(session, inputId, label = NULL, data = NULL, selected = NULL, options = list(), server = FALSE) { +updateVarSelectizeInput <- function(session = getDefaultReactiveDomain(), inputId, label = NULL, + data = NULL, selected = NULL, options = list(), server = FALSE) +{ + validate_session_object(session) + if (is.null(data)) { choices <- NULL } else { diff --git a/man-roxygen/update-input.R b/man-roxygen/update-input.R index b813ba6453..c71120743e 100644 --- a/man-roxygen/update-input.R +++ b/man-roxygen/update-input.R @@ -17,6 +17,6 @@ #' can be cleared by using \code{selected=character(0)}. #' #' @param session The \code{session} object passed to function given to -#' \code{shinyServer}. +#' \code{shinyServer}. Default is \code{getDefaultReactiveDomain()}. #' @param inputId The id of the input object. #' @param label The label to set for the input object. diff --git a/man/updateActionButton.Rd b/man/updateActionButton.Rd index 2471df864e..bf2cd0e5e0 100644 --- a/man/updateActionButton.Rd +++ b/man/updateActionButton.Rd @@ -5,13 +5,23 @@ \alias{updateActionLink} \title{Change the label or icon of an action button on the client} \usage{ -updateActionButton(session, inputId, label = NULL, icon = NULL) +updateActionButton( + session = getDefaultReactiveDomain(), + inputId, + label = NULL, + icon = NULL +) -updateActionLink(session, inputId, label = NULL, icon = NULL) +updateActionLink( + session = getDefaultReactiveDomain(), + inputId, + label = NULL, + icon = NULL +) } \arguments{ \item{session}{The \code{session} object passed to function given to -\code{shinyServer}.} +\code{shinyServer}. Default is \code{getDefaultReactiveDomain()}.} \item{inputId}{The id of the input object.} diff --git a/man/updateCheckboxGroupInput.Rd b/man/updateCheckboxGroupInput.Rd index 35f24f972d..b2e0ac7868 100644 --- a/man/updateCheckboxGroupInput.Rd +++ b/man/updateCheckboxGroupInput.Rd @@ -5,7 +5,7 @@ \title{Change the value of a checkbox group input on the client} \usage{ updateCheckboxGroupInput( - session, + session = getDefaultReactiveDomain(), inputId, label = NULL, choices = NULL, @@ -17,7 +17,7 @@ updateCheckboxGroupInput( } \arguments{ \item{session}{The \code{session} object passed to function given to -\code{shinyServer}.} +\code{shinyServer}. Default is \code{getDefaultReactiveDomain()}.} \item{inputId}{The id of the input object.} diff --git a/man/updateCheckboxInput.Rd b/man/updateCheckboxInput.Rd index 2832886bac..c98be6aade 100644 --- a/man/updateCheckboxInput.Rd +++ b/man/updateCheckboxInput.Rd @@ -4,11 +4,16 @@ \alias{updateCheckboxInput} \title{Change the value of a checkbox input on the client} \usage{ -updateCheckboxInput(session, inputId, label = NULL, value = NULL) +updateCheckboxInput( + session = getDefaultReactiveDomain(), + inputId, + label = NULL, + value = NULL +) } \arguments{ \item{session}{The \code{session} object passed to function given to -\code{shinyServer}.} +\code{shinyServer}. Default is \code{getDefaultReactiveDomain()}.} \item{inputId}{The id of the input object.} diff --git a/man/updateDateInput.Rd b/man/updateDateInput.Rd index ac5656a62d..683d092c3d 100644 --- a/man/updateDateInput.Rd +++ b/man/updateDateInput.Rd @@ -5,7 +5,7 @@ \title{Change the value of a date input on the client} \usage{ updateDateInput( - session, + session = getDefaultReactiveDomain(), inputId, label = NULL, value = NULL, @@ -15,7 +15,7 @@ updateDateInput( } \arguments{ \item{session}{The \code{session} object passed to function given to -\code{shinyServer}.} +\code{shinyServer}. Default is \code{getDefaultReactiveDomain()}.} \item{inputId}{The id of the input object.} diff --git a/man/updateDateRangeInput.Rd b/man/updateDateRangeInput.Rd index db8f3c8335..80b6e6034b 100644 --- a/man/updateDateRangeInput.Rd +++ b/man/updateDateRangeInput.Rd @@ -5,7 +5,7 @@ \title{Change the start and end values of a date range input on the client} \usage{ updateDateRangeInput( - session, + session = getDefaultReactiveDomain(), inputId, label = NULL, start = NULL, @@ -16,7 +16,7 @@ updateDateRangeInput( } \arguments{ \item{session}{The \code{session} object passed to function given to -\code{shinyServer}.} +\code{shinyServer}. Default is \code{getDefaultReactiveDomain()}.} \item{inputId}{The id of the input object.} diff --git a/man/updateNumericInput.Rd b/man/updateNumericInput.Rd index 749c2c22d1..f52a6d8182 100644 --- a/man/updateNumericInput.Rd +++ b/man/updateNumericInput.Rd @@ -5,7 +5,7 @@ \title{Change the value of a number input on the client} \usage{ updateNumericInput( - session, + session = getDefaultReactiveDomain(), inputId, label = NULL, value = NULL, @@ -16,7 +16,7 @@ updateNumericInput( } \arguments{ \item{session}{The \code{session} object passed to function given to -\code{shinyServer}.} +\code{shinyServer}. Default is \code{getDefaultReactiveDomain()}.} \item{inputId}{The id of the input object.} diff --git a/man/updateRadioButtons.Rd b/man/updateRadioButtons.Rd index a24577a9bd..87e2cf4b6f 100644 --- a/man/updateRadioButtons.Rd +++ b/man/updateRadioButtons.Rd @@ -5,7 +5,7 @@ \title{Change the value of a radio input on the client} \usage{ updateRadioButtons( - session, + session = getDefaultReactiveDomain(), inputId, label = NULL, choices = NULL, @@ -17,7 +17,7 @@ updateRadioButtons( } \arguments{ \item{session}{The \code{session} object passed to function given to -\code{shinyServer}.} +\code{shinyServer}. Default is \code{getDefaultReactiveDomain()}.} \item{inputId}{The id of the input object.} diff --git a/man/updateSelectInput.Rd b/man/updateSelectInput.Rd index 76e3f7d18f..0aa828eb4f 100644 --- a/man/updateSelectInput.Rd +++ b/man/updateSelectInput.Rd @@ -8,7 +8,7 @@ \title{Change the value of a select input on the client} \usage{ updateSelectInput( - session, + session = getDefaultReactiveDomain(), inputId, label = NULL, choices = NULL, @@ -16,7 +16,7 @@ updateSelectInput( ) updateSelectizeInput( - session, + session = getDefaultReactiveDomain(), inputId, label = NULL, choices = NULL, @@ -26,7 +26,7 @@ updateSelectizeInput( ) updateVarSelectInput( - session, + session = getDefaultReactiveDomain(), inputId, label = NULL, data = NULL, @@ -34,7 +34,7 @@ updateVarSelectInput( ) updateVarSelectizeInput( - session, + session = getDefaultReactiveDomain(), inputId, label = NULL, data = NULL, @@ -45,7 +45,7 @@ updateVarSelectizeInput( } \arguments{ \item{session}{The \code{session} object passed to function given to -\code{shinyServer}.} +\code{shinyServer}. Default is \code{getDefaultReactiveDomain()}.} \item{inputId}{The id of the input object.} diff --git a/man/updateSliderInput.Rd b/man/updateSliderInput.Rd index 0c98753751..eaa1f1798a 100644 --- a/man/updateSliderInput.Rd +++ b/man/updateSliderInput.Rd @@ -5,7 +5,7 @@ \title{Update Slider Input Widget} \usage{ updateSliderInput( - session, + session = getDefaultReactiveDomain(), inputId, label = NULL, value = NULL, @@ -18,7 +18,7 @@ updateSliderInput( } \arguments{ \item{session}{The \code{session} object passed to function given to -\code{shinyServer}.} +\code{shinyServer}. Default is \code{getDefaultReactiveDomain()}.} \item{inputId}{The id of the input object.} diff --git a/man/updateTabsetPanel.Rd b/man/updateTabsetPanel.Rd index ad5d09ddae..9e5139a367 100644 --- a/man/updateTabsetPanel.Rd +++ b/man/updateTabsetPanel.Rd @@ -6,15 +6,27 @@ \alias{updateNavlistPanel} \title{Change the selected tab on the client} \usage{ -updateTabsetPanel(session, inputId, selected = NULL) +updateTabsetPanel( + session = getDefaultReactiveDomain(), + inputId, + selected = NULL +) -updateNavbarPage(session, inputId, selected = NULL) +updateNavbarPage( + session = getDefaultReactiveDomain(), + inputId, + selected = NULL +) -updateNavlistPanel(session, inputId, selected = NULL) +updateNavlistPanel( + session = getDefaultReactiveDomain(), + inputId, + selected = NULL +) } \arguments{ \item{session}{The \code{session} object passed to function given to -\code{shinyServer}.} +\code{shinyServer}. Default is \code{getDefaultReactiveDomain()}.} \item{inputId}{The id of the \code{tabsetPanel}, \code{navlistPanel}, or \code{navbarPage} object.} diff --git a/man/updateTextAreaInput.Rd b/man/updateTextAreaInput.Rd index c23afa4925..77a2495d25 100644 --- a/man/updateTextAreaInput.Rd +++ b/man/updateTextAreaInput.Rd @@ -5,7 +5,7 @@ \title{Change the value of a textarea input on the client} \usage{ updateTextAreaInput( - session, + session = getDefaultReactiveDomain(), inputId, label = NULL, value = NULL, @@ -14,7 +14,7 @@ updateTextAreaInput( } \arguments{ \item{session}{The \code{session} object passed to function given to -\code{shinyServer}.} +\code{shinyServer}. Default is \code{getDefaultReactiveDomain()}.} \item{inputId}{The id of the input object.} diff --git a/man/updateTextInput.Rd b/man/updateTextInput.Rd index be68ab25ca..ec04b88250 100644 --- a/man/updateTextInput.Rd +++ b/man/updateTextInput.Rd @@ -5,7 +5,7 @@ \title{Change the value of a text input on the client} \usage{ updateTextInput( - session, + session = getDefaultReactiveDomain(), inputId, label = NULL, value = NULL, @@ -14,7 +14,7 @@ updateTextInput( } \arguments{ \item{session}{The \code{session} object passed to function given to -\code{shinyServer}.} +\code{shinyServer}. Default is \code{getDefaultReactiveDomain()}.} \item{inputId}{The id of the input object.}