jQuery 验证 R/Shiny 应用程序中的表单

jQuery validation of form in R/Shiny application

是否可以使用 jQuery validation plugin 来验证 R/Shiny 应用程序中的表单?

我想在我的应用程序中利用这个很棒的软件包

我无法在下面找到一个最小的示例来工作,而且我无法确定问题所在。

library(shiny)
library(shinydashboard)

ui <- 

dashboardPage(
dashboardHeader(),
dashboardSidebar(),
dashboardBody(
fluidPage(
includeScript('jquery.validate.min.js'),
    tags$form(id='myform',
     textInput('field1','label'),
     textInput('field2','label2'),
     submitButton('submit')
    ),


tags$script('
$(document).ready(function () {
$(\'#myform\').validate({ // initialize the plugin
           rules: {
             field1: {
               required: true,
               email: true
             },
             field2: {
               required: true,
               minlength: 5
             }
           }
           });

  });               

           ')
 )
 )
 )

 server <- function(input, output) {

 # Once form is validated, do something 

 }

# Run the application 
shinyApp(ui = ui, server = server)

我不确定它是否有效,但使用 Dean Attali 的 shinyJS 包可能会更好,特别是 extendshinyjs()。

https://deanattali.com/shinyjs/ and https://deanattali.com/shinyjs/extend

查看 Dean 网站上的这个模板,看看您可以使用 $(document).ready() 验证部分进行的明显交换:

jscode <- "
shinyjs.init = function() {
  $(document).keypress(function(e) { alert('Key pressed: ' + e.which); });
}"

shinyApp(
  ui = fluidPage(
    useShinyjs(),
    extendShinyjs(text = jscode),
    "Press any key"
  ),
  server = function(input, output) {}
)

如果有效,请post返回此处。

这是可能的,但在 Shiny 中做起来非常痛苦。根据 jQuery 验证插件文档:

Mandated: A 'name' attribute is required for all input elements needing validation, and the plugin will not work without this. A 'name' attribute must also be unique to the form, as this is how the plugin keeps track of all input elements.

https://jqueryvalidation.org/reference/

Shiny textInputs 没有 'name' 属性,也没有提供添加属性的简单方法。你有点不得不绕过它——要么构建你自己的 textInput,要么修改现有的。

第二种痛苦:Shiny 捕获并专门处理表单提交以使 submitButton 工作。这意味着我们不能依赖 jQuery.Validate 的内置行为来防止提交无效表单。相反,我们必须解决它,使用类似 showErrors 回调的东西来简单地在表单无效时引发一个标志。很难防止将无效表单发送到服务器,但至少您可以在对输入进行任何操作之前检查输入是否有效。

此示例展示了两种添加规则的方法:纯标记和自定义规则对象(如您的示例中所示)。我推荐标记只是为了便于使用。有关示例,请参阅 https://jqueryvalidation.org/documentation/

library(shiny)
library(shinydashboard)

# Use a CDN for the example, but can also be sourced locally
jQueryValidateLib <- htmltools::htmlDependency(
  "jquery.validate", "1.17.0",
  src = c(href = "https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.17.0"),
  script = "jquery.validate.min.js"
)

# Form validity status is accessible at input$formId_valid
validatedForm <- function(formId, ..., rules = NULL, options = list()) {
  options$rules <- rules
  tagList(
    jQueryValidateLib,
    tags$form(id = formId, ...),
    tags$script(HTML(sprintf("
      $(function () {
        var formId = '%s';
        var options = %s;

        options.showErrors = function(e) {
          var isValid = this.numberOfInvalids() === 0;
          Shiny.onInputChange(formId + '_valid', isValid);
          this.defaultShowErrors();
        };

        $('#' + formId).validate(options);
      });
      ", formId, toJSON(options))
    ))
  )
}

# Like textInput but allows adding attributes to the input element through ...
validatedTextInput <- function(inputId, label, value = "", width = NULL,
                               placeholder = NULL, ...) {
  inputContainer <- textInput(inputId, label, value = value,
                              width = width, placeholder = placeholder)
  inputElement <- inputContainer$children[[2]]

  attribs <- c(inputElement$attribs, list(name = inputId, ...))
  attribs[duplicated(names(attribs), fromLast = TRUE)] <- NULL
  inputElement$attribs <- attribs

  inputContainer$children[[2]] <- inputElement
  inputContainer
}

toJSON <- function(x, auto_unbox = TRUE, ...) {
  jsonlite::toJSON(x, auto_unbox = auto_unbox)
}

ui <- dashboardPage(
  dashboardHeader(),
  dashboardSidebar(),
  dashboardBody(
    validatedForm(
      "myform",
      validatedTextInput("field1", "field1", required = TRUE, type = "email"),
      validatedTextInput("field2", "field2"),
      submitButton("Submit"),
      rules = list(field2 = list(required = TRUE, minlength = 5))
    ),

    verbatimTextOutput("fields")
  )
)

server <- function(input, output) {
  output$fields <- renderPrint({
    cat(
      paste("field1:", input$field1),
      paste("field2:", input$field2),
      paste("myform_valid:", input$myform_valid),
      sep = "\n"
    )
  })
}

shinyApp(ui = ui, server = server)

这是生成的 HTML 的样子:

<form id="myform">
  <div class="form-group shiny-input-container">
    <label for="field1">field1</label>
    <input id="field1" class="form-control" value="" name="field1" required="TRUE" type="email"/>
  </div>
  <div class="form-group shiny-input-container">
    <label for="field2">field2</label>
    <input id="field2" type="text" class="form-control" value="" name="field2"/>
  </div>
  <div>
    <button type="submit" class="btn btn-primary">Submit</button>
  </div>
</form>
<script>
$(function () {
  var formId = 'myform';
  var options = {"rules":{"field2":{"required":true,"minlength":5}}};

  options.showErrors = function(e) {
    var isValid = this.numberOfInvalids() === 0;
    Shiny.onInputChange(formId + '_valid', isValid);
    this.defaultShowErrors();
  };

  $('#' + formId).validate(options);
});
</script>