In brief, what is the preferred mode of running validation checks on input variables in Shiny?
An example below:
# need + validate -----------------------------------------------------------
validate(
need(
check_mzr_object(ms_object$mzr_connection),
"Wasn't able to connect to MS file"
)
)
validate(
need(!is.null(input$ppm_input), "ppm_input must not be null"),
need(input$ppm_input > 0L, "ppm_input must be > 0")
)
# if + validate -----------------------------------------------------------
if (!check_mzr_object(ms_object$mzr_connection)) {
validate("Wasn't able to connect to MS file")
}
if (is.null(input$ppm_input)) {
validate("ppm_input must not be null")
} else if (input$ppm_input <= 0L) {
validate("ppm_input must be > 0")
}
I agree with both points of view here. I would add that shiny-specific functions like validate+need or req are not as explicit about the condition they're testing. With a if statement, you have to be very explicit about nulls, empty, length, etc whereas the shiny functions are convenience shortcuts - they're more compact but also less precise in my opinion.
Yeah, need is just a convenience function, but aren't most things?:
shiny::need
function (expr, message = paste(label, "must be provided"), label)
{
force(message)
if (!isTruthy(expr))
return(message)
else return(invisible(NULL))
}
#--------------------------
shiny::isTruthy
function (x)
{
if (inherits(x, "try-error"))
return(FALSE)
if (!is.atomic(x))
return(TRUE)
if (is.null(x))
return(FALSE)
if (length(x) == 0)
return(FALSE)
if (all(is.na(x)))
return(FALSE)
if (is.character(x) && !any(nzchar(stats::na.omit(x))))
return(FALSE)
if (inherits(x, "shinyActionButtonValue") && x == 0)
return(FALSE)
if (is.logical(x) && !any(stats::na.omit(x)))
return(FALSE)
return(TRUE)
}
To be clear, I
think your approach is fine, and I'm not saying you
shouldn't use it. But I currently don't plan to teach it
because I think it's a distraction. (Also if you validation is
that complex wouldn't you want it be in a function so you could
test it outside of Shiny?)
I usually separate things so that anything that fails in Shiny uses validate(need), so reactives, inputs, are tested with that so that custom CSS can be applied and, more importantly, helpful messages can be displayed not just "on error" but as a suggestive message of how to proceed through the app.
I don't
think so, in almost every web interface registering a password you get
instant feedbacks about the password you're setting. For example
on GitHub: pic.twitter.com/kmCxcmxHDX
If a function is vitally important, and its logic is not dependent on Shiny, I write as a function (like writing a package) that has zero dependency on shiny. In that case a regular if(FALSE)stop("message") is used, no validation(). An added benefit is now this function can be borrowed for use elsewhere without calling a dependency on Shiny.
Also, pulling out app functionality into functions that are independent of Shiny is a fantastic way to tell when a new, maybe small, package should be created that contains a subset of functions that the larger work uses.