Overview
modUpload uses {shinyvalidate} for comprehensive input validation across the multi-step wizard. This guide explains the validation architecture and how to work with validation rules.
Validation Architecture
The wizard uses two validators, one for each step:
- Step 1 Validator - Required fields (artwork name, collection, files)
- Step 2 Validator - Optional fields (additional variants, story)
Step 1: Required Fields
validator_step_1 <- function() {
iv <- shinyvalidate::InputValidator$new()
# Artwork name validation
iv$add_rule("art_name", shinyvalidate::sv_required())
iv$add_rule("art_name", shinyvalidate::sv_regex(
"[a-zA-Z0-9\_\-\ ]+",
"No special characters allowed"
))
# Collection selection
iv$add_rule("art_collection", shinyvalidate::sv_required())
# File validation using ckfile()
iv$add_rule("stats_file", shinyvalidate::sv_required(
"Expecting a single file ending in .png or .jpeg",
function(x) nrow(x) == 1 && (ckfile(x, "png") || ckfile(x, "jpeg"))
))
iv$add_rule("image_file", shinyvalidate::sv_required(
"Expecting a single file ending in .png",
function(x) nrow(x) == 1 && ckfile(x, "png")
))
iv$add_rule("raw_file", shinyvalidate::sv_required(
"Expecting a single file ending in .procreate",
function(x) nrow(x) == 1 && ckfile(x, "procreate")
))
iv$add_rule("video_file", shinyvalidate::sv_required(
"Expecting a single file ending in .mp4",
function(x) nrow(x) == 1 && ckfile(x, "mp4")
))
iv
}Step 2: Optional Fields
validator_step_2 <- function() {
iv <- shinyvalidate::InputValidator$new()
# Optional zip file for additional frames
iv$add_rule("frames_zip", shinyvalidate::sv_optional())
iv$add_rule("frames_zip", ~ if (!ckfile(., "zip")) "Zip folder required")
iv
}The ckfile() Helper
The ckfile() function validates uploaded files:
ckfile <- function(x, ext) {
# Extract file extension
fileExt <- function(filename) {
stringr::str_split_i(filename, "\\\\|/", -1) |>
stringr::str_extract("(?<=\.).+(?=$)")
}
# Check file structure and extension
if (is.null(x)) return(FALSE)
all(names(x) %in% c("name", "size", "type", "datapath")) &&
x$size > 500 && # Minimum 500 bytes
fileExt(stringr::str_to_lower(x$name)) == ext
}Validation Checks
ckfile() validates:
- Structure - File input has expected columns (name, size, type, datapath)
- Size - File is larger than 500 bytes (not empty)
- Extension - File extension matches expected type
Using Validators in Server
The validators are initialized and used in the server:
modUploadServer <- function(id, r) {
shiny::moduleServer(id, function(input, output, session) {
# Initialize validators
iv_1 <- validator_step_1()
iv_2 <- validator_step_2()
# On submit, validate Step 1
shiny::observeEvent(input$btn_submit, {
iv_1$enable()
if (iv_1$is_valid()) {
iv_1$disable()
shiny::removeModal()
# Proceed with upload
} else {
# Validation errors displayed automatically
}
})
# On cancel, disable validators
shiny::observeEvent(input$btn_cancel, {
iv_1$disable()
iv_2$disable()
shiny::removeModal()
})
})
}Validation Flow
User opens wizard modal
|
Step 1: Fill required fields
|
Click Submit
|
iv_1$enable() -> Display validation errors
|
iv_1$is_valid()?
|--- No: Show errors, stay on form
|--- Yes: iv_1$disable(), proceed to processing
Best Practices
- Enable validators on submit, not on input change
- Disable validators when modal is closed or cancelled
- Use sv_required() with custom predicates for file validation
- Provide clear error messages that guide users to fix issues
Supported File Types
| File Type | Extension | Validation |
|---|---|---|
| Artwork Image | .png | Required, single file |
| Canvas Stats | .png, .jpeg | Required, single file |
| Canvas File | .procreate | Required, single file |
| Timelapse Video | .mp4 | Required, single file |
| Frames Archive | .zip | Optional |
| Variants | .png | Optional, multiple files |
Related Functions
-
validator_step_1()- Step 1 input validator -
validator_step_2()- Step 2 input validator -
ckfile()- File extension validation helper
