为啥不显眼的验证直到 AJAX 发布之后才起作用?

Posted

技术标签:

【中文标题】为啥不显眼的验证直到 AJAX 发布之后才起作用?【英文标题】:Why doesn't unobtrusive validation work until after an AJAX post?为什么不显眼的验证直到 AJAX 发布之后才起作用? 【发布时间】:2013-10-08 01:54:37 【问题描述】:

我有一个 MVC3 项目,我想在其中使用自定义验证属性进行客户端和服务器端处理。我已按照http://thewayofcode.wordpress.com/2012/01/18/custom-unobtrusive-jquery-validation-with-data-annotations-in-mvc-3/ 中的步骤进行操作。那是一个很棒的教程,实际上效果很好。

我遇到的唯一问题是,我的验证似乎直到表单提交之后才触发。我有客户端和服务器端验证。服务器端验证是验证属性和自定义验证的组合(例如,有时我必须根据数据库中的某些内容检查输入值)。当我第一次单击表单上的保存按钮(使用 Ajax.BeginForm)时,该帖子发生在服务器上,服务器端验证启动并返回验证消息,因为输入无效。如果我将表单输入保持原样并再次单击“保存”按钮,客户端验证将正常工作并阻止发布。

什么可能导致客户端验证被跳过,直到表单发布之后?

我的自定义验证属性:

    public class RequiredIfContainsAttribute : ValidationAttribute, IClientValidatable
    
        private RequiredAttribute _innerAttribute = new RequiredAttribute();

        public string DependentProperty  get; set; 
        public string ComparisonValue  get; set; 

        public RequiredIfContainsAttribute(string dependentProperty, string comparisonValue)
        
            DependentProperty = dependentProperty;
            ComparisonValue = comparisonValue;
        

        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        
            // get a reference to the property this validation depends upon
            var containerType = validationContext.ObjectInstance.GetType();
            var field = containerType.GetProperty(DependentProperty);

            if (field != null)
            
                // get the value of the dependent property
                var dependentValue = field.GetValue(validationContext.ObjectInstance, null);

                // this validation only works if the comparison field is a string
                if (dependentValue.GetType() != typeof(string))
                
                    return ValidationResult.Success;
                

                var dependentString = (string) dependentValue;

                // check whether the string to check contains the comparison value
                if (dependentString.Contains(ComparisonValue))
                
                    // if the string to check contains the comparison value, the attribute becomes required and must now be validated
                    if (!_innerAttribute.IsValid(value))
                    
                        // validation failed - return an error
                        return new ValidationResult(ErrorMessage, new[] validationContext.MemberName);
                    
                
            

            return ValidationResult.Success;
        

        public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata,
                                                                           ControllerContext context)
        
            var rule = new ModelClientValidationRule()
                
                    ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
                    ValidationType = "requiredifcontains"
                ;

            var depProp = BuildDependentPropertyId(metadata, context as ViewContext);

            rule.ValidationParameters.Add("dependentproperty", depProp);
            rule.ValidationParameters.Add("comparisonvalue", ComparisonValue);

            yield return rule;
        

        private string BuildDependentPropertyId(ModelMetadata metadata, ViewContext viewContext)
        
            string depProp = viewContext.ViewData.TemplateInfo.GetFullhtmlFieldId(DependentProperty);

            var thisField = metadata.PropertyName + "_";
            if (depProp.StartsWith(thisField))
            
                // strip it off again
                depProp = depProp.Substring(thisField.Length);
            

            return depProp;
        
    

我的模型属性:

    [RequiredIfContains("FirstName", "Mickey", ErrorMessage = "The date of birth is required when the first name is Mickey")]
    public DateTime DateOfBirth  get; set; 

我的自定义 js 添加验证器:

$.validator.addMethod('requiredifcontains',
function (value, element, parameters) 
    console.log("requiredifcontains starting");
    var id = '#' + parameters['dependentproperty'];

    // get the target value (as a string, 
    // as that's what actual value will be)
    var comparisonvalue = parameters['comparisonvalue'];
    comparisonvalue =
      (comparisonvalue == null ? '' : comparisonvalue).toString();

    // get the actual value of the target control
    // note - this probably needs to cater for more 
    // control types, e.g. radios
    var control = $(id);
    var inputValue = 'empty';
    if (control.is('input:text')) 
        inputValue = control.text();
     else if (control.is('select')) 
        inputValue = $(id + " option:selected").text();
    

    // if the input control wasn't found (possibly because the type wasn't checked for) then we can't compare so just return true
    if (inputValue == 'empty') 
        return true;
    

    // if the condition is true, reuse the existing 
    // required field validator functionality
    console.log("requiredifcontains performing underlying validation");
    if (inputValue.indexOf(comparisonvalue) > -1)
        return $.validator.methods.required.call(
          this, value, element, parameters);

    console.log("requiredifcontains returning true");
    return true;

);

$.validator.unobtrusive.adapters.add(
'requiredifcontains',
['dependentproperty', 'comparisonvalue'],
function (options) 
    options.rules['requiredifcontains'] = 
        dependentproperty: options.params['dependentproperty'],
        targetvalue: options.params['comparisonvalue']
    ;
    options.messages['requiredifcontains'] = options.message;
);

一种人为的观点,几乎与失败的观点相同:

@
var options = new AjaxOptions()
    
        HttpMethod = "Post",
        UpdateTargetId = "personalInfoDiv",
        OnSuccess = "FormSubmitSuccess()"
    ;


<div id="personalInfoDiv">
@using (Ajax.BeginForm("PersonalInformationDetail", "PersonalInformation", null, options, new @style = "height:100%", @id = "PersonalInformationForm"))

    @Html.ValidationSummary(false)
    @Html.EditorForModel()
    <div style="float:left; position:relative;">
        <input type="button" value="Save" style="width:125px;" id="Save" onclick="saveClick(this)" />
    </div>

</div>

保存点击的javascript和成功方法:

function saveClick(e) 
var firstName = $("#FirstName").val();
var result = true;
if (firstName == '') 
    result = confirm("First name is not required but is recommended.  Choose OK to save anyway or CANCEL to add a first name.");


if (result) 
    $("#PersonalInformationForm").submit();



function FormSubmitSuccess(result) 
// do some stuff after the form submits

我一直在寻找这个,我发现的大多数解决方案都是与我的问题相反的解决方案。我已经记录了 form.validate() 的结果,我看到我第一次点击保存时没有错误,但第二次(发布后)错误就在那里。

这可能是我想念的一些简单的东西,但我不知道还能在这里尝试什么,而且我的时间不多了。

这是我在这里的第一篇文章,所以如果我忽略了包含相关内容,请告诉我,我可以更新我的问题。

【问题讨论】:

【参考方案1】:

我不确定它为什么第二次起作用,为了使客户端验证起作用,您需要通过返回 false 或在 saveClick() 中执行 e.preventDefault() 来防止提交表单方法。

【讨论】:

实际上,我认为我不需要阻止默认设置,因为我的保存按钮实际上是一个 你是对的。再次查看问题后,我想我看到了可能发生的情况,MVC3 将尝试使用 Ajax.BeginForm 将表单提交到服务器进行验证。我相信如果您使用 HTML.BeginForm 那么 jQuery 验证器将有机会启动并停止传统的表单提交。这可能是它第二次工作的原因,因为它可能知道不要尝试自己的 Ajax POST。让我知道这是否有效,我会发布一个新答案。

以上是关于为啥不显眼的验证直到 AJAX 发布之后才起作用?的主要内容,如果未能解决你的问题,请参考以下文章

jquery datepicker上的自定义动态验证直到第二个焦点才起作用

为啥我的 C++ 函数只有放在 main() 之后才起作用?

PartialView 和不显眼的客户端验证不起作用

jquery.validate.js第一次提交验证不起作用,刷新之后再提交才起作用,请各位帮忙看看是怎么回事!

jQueryUI 内联 DatePicker 直到我更改月份后才起作用

Primefaces数据表排序和过滤直到rowSelect才起作用