I'm trying to use the R package shinyvalidate to do something very simple: check if a password field and a re-enter password field are identical.
The most intuitive way to me to try to do this was just to add a rule directly that the two password inputs must match:
library(shiny)
library(shinyvalidate)
# Define ui ----
ui <- fluidPage(
uiOutput("registration_page")
)
# Define server ----
server <- server <- function(input, output) {
output$registration_page <- renderUI({
fluidPage(
textInput("regis_username", "Username:"),
passwordInput("regis_password", "Password:"),
passwordInput("regis_password2", "Re-enter password:"),
actionButton("submit_registration", "Submit Registration")
)
})
#Registration input validator
reg_iv <- InputValidator$new()
reg_iv$add_rule("regis_password", sv_required())
reg_iv$add_rule("regis_password2", sv_required())
reg_iv$add_rule("regis_password2",
sv_equal(input$regis_password, message_fmt = "Must match password"))
reg_iv$enable()
}
shinyApp(ui = ui, server = server)
However, this code fails with the error message Error in input$regis_password: Can't access reactive value 'regis_password' outside of reactive consumer. Do you need to wrap inside reactive() or observe()?
So then I wrap the rules in an observeEvent call, so they're reactive and update every time regis_password
is edited.
library(shiny)
library(shinyvalidate)
# Define ui ----
ui <- fluidPage(
uiOutput("registration_page")
)
# Define server ----
server <- server <- function(input, output) {
output$registration_page <- renderUI({
fluidPage(
textInput("regis_username", "Username:"),
passwordInput("regis_password", "Password:"),
passwordInput("regis_password2", "Re-enter password:"),
actionButton("submit_registration", "Submit Registration")
)
})
#Registration input validator
reg_iv <- InputValidator$new()
reg_iv$add_rule("regis_password", sv_required())
observeEvent(input$regis_password, {
reg_iv$add_rule("regis_password2", sv_required())
reg_iv$add_rule("regis_password2",
sv_equal(input$regis_password, message_fmt = "Must match password"))
})
reg_iv$enable()
}
shinyApp(ui = ui, server = server)
Now the Must match password
validation failure is always showing, I think because every change to regis_password
results in a new rule but the old match rules are still there, so there's no way to match them all?
So then I make a new validator just for the 2nd password:
library(shiny)
library(shinyvalidate)
# Define ui ----
ui <- fluidPage(
uiOutput("registration_page")
)
# Define server ----
server <- server <- function(input, output) {
output$registration_page <- renderUI({
fluidPage(
textInput("regis_username", "Username:"),
passwordInput("regis_password", "Password:"),
passwordInput("regis_password2", "Re-enter password:"),
actionButton("submit_registration", "Submit Registration")
)
})
#Registration input validator
reg_iv <- InputValidator$new()
reg_iv$add_rule("regis_password", sv_required())
observeEvent(input$regis_password, {
reg_password2_iv <- InputValidator$new()
reg_password2_iv$add_rule("regis_password2", sv_required())
reg_password2_iv$add_rule(
"regis_password2",
sv_equal(input$regis_password, message_fmt = "Must match password"))
reg_password2_iv$enable()
})
reg_iv$enable()
observeEvent(input$submit_registration, {
req(reg_iv$is_valid())
req(reg_password2_iv$is_valid())
})
}
shinyApp(ui = ui, server = server)
This has the desired effect for the validator rules, but produces an error once we try to actually check whether the validator rules are met: the line req(reg_password2_iv$is_valid())
produces the error message Error in observe: object 'reg_password2_iv' not found
with the trace below.
89: eval
88: eval
85: dotloop
84: req
83: observe [#33]
82: <observer:observeEvent(input$submit_registration)>
3: runApp
2: print.shiny.appobj
1: <Anonymous>
So, what am I doing wrong? I've scoured the internet as best I can but I can't find a simple example where two input$ fields are compared against each other reactively with shinyvalidate
It seems to be that sv_equal()
is not working with reactive values. However, instead it is possible to just pass a custom function to add_rule()
which does the comparison job:
reg_iv$add_rule("regis_password2",
function(value, input){
if (value != input$regis_password) {
"Must match password"
}
}, input)
library(shiny)
library(shinyvalidate)
# Define ui ----
ui <- fluidPage(
uiOutput("registration_page")
)
# Define server ----
server <- server <- function(input, output) {
output$registration_page <- renderUI({
fluidPage(
textInput("regis_username", "Username:"),
passwordInput("regis_password", "Password:"),
passwordInput("regis_password2", "Re-enter password:"),
actionButton("submit_registration", "Submit Registration")
)
})
#Registration input validator
reg_iv <- InputValidator$new()
reg_iv$add_rule("regis_password", sv_required())
reg_iv$add_rule("regis_password2", sv_required())
reg_iv$add_rule("regis_password2",
function(value, input){
if (value != input$regis_password) {
"Must match password"
}
}, input)
reg_iv$enable()
}
shinyApp(ui = ui, server = server)