为什么在AJAX发布之后才进行不显眼的验证?

时间:2013-10-01 19:15:47

标签: jquery ajax asp.net-mvc-3 validation

我有一个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()的结果,我看到第一次单击Save时没有错误,但是第二次(在post之后)错误就在那里。

这可能是我想念的简单事情,但我不知道还有什么可以尝试,而且我已经没时间了。

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

1 个答案:

答案 0 :(得分:0)

我不确定为什么它第二次工作,为了使客户端验证工作,你将希望通过返回false或在{{1中执行e.preventDefault()来阻止提交表单方法。