我正在尝试创建在ASP.NET中有条件地验证模型的能力。我让它用于服务器端验证,但我无法弄清楚如何使它与客户端验证一起工作。
例如,
public class Student
{
[Required]
public string Name { get; set; }
public bool RequiresAddress { get; set; }
[RequiredIf("RequiresAddress", true)]
public string Address { get; set; }
}
这是我的RequiredIf属性(创建以下this post):
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
public sealed class RequiredIfAttribute : ValidationAttribute
{
public string DependentName;
public object DependentValue;
public RequiredIfAttribute(string PropertyName, object PropertyValue, string ErrorMessage)
{
this.DependentName = PropertyName;
this.DependentValue = PropertyValue;
this.ErrorMessage = ErrorMessage;
}
public override bool IsValid(object value)
{
if (value == null) { return false; }
string ValueString = value as string;
if (ValueString != null) { return (ValueString.Trim().Length != 0); }
return true;
}
}
然后我有了这个验证器(我使用DataAnnotationsModelValidatorProvider在Global.asax.cs中注册):
public class RequiredIfValidator : DataAnnotationsModelValidator<RequiredIfAttribute>
{
public RequiredIfValidator(ModelMetadata metadata, ControllerContext context, RequiredIfAttribute attribute)
: base(metadata, context, attribute)
{
}
public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
{
var Rule = new ModelClientValidationRule
{
ErrorMessage = ErrorMessage,
ValidationType = "requiredif",
};
var viewContext = (ControllerContext as ViewContext);
string depProp = viewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(Attribute.DependentName);
Rule.ValidationParameters.Add("dependentname", depProp);
Rule.ValidationParameters.Add("dependentvalue", Attribute.DependentValue);
yield return Rule;
}
public override IEnumerable<ModelValidationResult> Validate(object container)
{
var Field = Metadata.ContainerType.GetProperty(Attribute.DependentName);
if (Field != null)
{
var Value = Field.GetValue(container, null);
if ((Value == null && Attribute.DependentValue == null) || (Value != null && Value.Equals(Attribute.DependentValue)))
{
if (!Attribute.IsValid(Metadata.Model))
{
yield return new ModelValidationResult { Message = ErrorMessage };
}
}
}
}
}
最后,在我看来,我尝试用jQuery注册客户端验证器:
if (typeof (jQuery) !== "undefined" && typeof (jQuery.validator) !== "undefined") {
(function ($) {
$.validator.addMethod('requiredif', function (value, element, parameters) {
var id = '#' + parameters['dependentname'];
var dependentvalue = parameters['dependentvalue'];
dependentvalue = (dependentvalue == null ? '' : dependentvalue).toString();
var actualvalue = $(id).val();
if (dependentvalue === actualvalue) {
return $.validator.methods.required.call(this, value, element, parameters);
}
else {
return true;
}
});
})(jQuery);
}
就像我说的那样,服务器端验证工作完美无缺,但客户端似乎根本没有工作。我是一个使用JavaScript的n00b,所以我不知道如何解决这个问题 - 我在这里缺少什么?
我摆脱了Validator类,并在自定义属性上实现了IClientValidatable。这是我的新RequiredIfAttribute。同样,服务器端的所有内容似乎都按预期工作,但客户端无法正常工作。
从我的观点可以看出,我正在使用DevExpress来构建表单。
P.S。 The zipped solution can be found here.
public class RequiredIfAttribute : ValidationAttribute, IClientValidatable
{
public string DependentName { get; set; }
public object DependentValue { get; set; }
public RequiredIfAttribute(string DependentName, object DependentValue)
{
this.DependentName = DependentName;
this.DependentValue = DependentValue;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata Metadata, ControllerContext Context)
{
var rule = new ModelClientValidationRule();
rule.ErrorMessage = FormatErrorMessage(Metadata.GetDisplayName());
rule.ValidationParameters.Add("dependentname", DependentName);
rule.ValidationParameters.Add("dependentvalue", DependentValue);
rule.ValidationType = "requiredif";
yield return rule;
}
public override bool IsValid(object value)
{
if (value == null) { return false; }
string ValueString = value as string;
if (ValueString != null) { return (ValueString.Trim().Length != 0); }
return true;
}
protected override ValidationResult IsValid(object Value, ValidationContext Context)
{
var ContainerType = Context.ObjectInstance.GetType();
var Field = ContainerType.GetProperty(this.DependentName);
if (Field != null)
{
var DependentValue = Field.GetValue(Context.ObjectInstance, null);
if ((DependentValue == null && DependentValue == null) ||
(DependentValue != null && DependentValue.Equals(this.DependentValue)))
{
if (!IsValid(Value))
{
//return new ValidationResult(ErrorMessage, new[] { Context.MemberName });
return new ValidationResult(FormatErrorMessage(Context.DisplayName));
}
}
}
return ValidationResult.Success;
}
public string GetPropertyID(ModelMetadata Metadata, ViewContext Context)
{
string DependentID = Context.ViewData.TemplateInfo.GetFullHtmlFieldId(this.DependentName);
var Fie1ldID = Metadata.PropertyName + "_";
return (DependentID.StartsWith(FieldID))
? DependentID.Substring(FieldID.Length)
: DependentID;
}
}
然后这是我的View类以及相关的JavaScript:
<script type="text/javascript">
function OnValueChanged(s, e) {
var Result = ASPxClientCheckBox.Cast(s).GetValue();
StudentForm.GetItemByName("Address").SetVisible(!Result);
}
</script>
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
@Html.ValidationSummary(false)
@Html.DevExpress().FormLayout(Form =>
{
Form.Name = "StudentForm";
Form.Items.Add(x => x.Name, Item =>
{
(Item.NestedExtensionSettings as TextBoxSettings).ShowModelErrors = true;
(Item.NestedExtensionSettings as TextBoxSettings).Properties.ValidationSettings.ErrorDisplayMode = ErrorDisplayMode.ImageWithTooltip;
(Item.NestedExtensionSettings as TextBoxSettings).Properties.ValidationSettings.Display = Display.Dynamic;
});
Form.Items.Add(x => x.Address, Item =>
{
Item.Name = "Address";
(Item.NestedExtensionSettings as TextBoxSettings).ShowModelErrors = true;
(Item.NestedExtensionSettings as TextBoxSettings).Properties.ValidationSettings.ErrorDisplayMode = ErrorDisplayMode.ImageWithTooltip;
(Item.NestedExtensionSettings as TextBoxSettings).Properties.ValidationSettings.Display = Display.Dynamic;
});
Form.Items.Add(x => x.AddressHidden, Item =>
{
Item.Name = "AddressHidden";
(Item.NestedExtensionSettings as CheckBoxSettings).Properties.ClientSideEvents.Init = "OnValueChanged";
(Item.NestedExtensionSettings as CheckBoxSettings).Properties.ClientSideEvents.ValueChanged = "OnValueChanged";
});
Form.Items.Add(x => x.AddressRequired);
Form.Items.Add(x =>
{
x.NestedExtensionType = FormLayoutNestedExtensionItemType.Button;
ButtonSettings Settings = (ButtonSettings)x.NestedExtensionSettings;
Settings.UseSubmitBehavior = true;
Settings.Text = "Submit";
Settings.Name = "SubmitButton";
});
}).Bind(Model).GetHtml()
}
<script type="text/javascript">
jQuery.validator.unobtrusive.adapters.add('requiredif', ['dependentname', 'dependentvalue'], function (options) {
options.rules["requiredif"] = true;
options.messages["requiredif"] = options.message;
});
$.validator.addMethod('requiredif', function (value, element, params) {
var id = '#' + $(element).attr("data-val-requiredif-dependentname");
var dependentvalue = $(element).attr("data-val-requiredif-dependentvalue");
var actualvalue = $(id).is(":checked");
if (dependentvalue == "True" && actualvalue && value.length <= 0) {
return false;
}
return true;
});
</script>
答案 0 :(得分:1)
如果您想通过附加属性来查看您的RequiredIfAttribute
必须实现IClientValidatable
界面。可以通过添加此方法来完成:
public IEnumerable<ModelClientValidationRule> GetClientValidationRules( ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule();
rule.ErrorMessage = FormatErrorMessage(metadata.GetDisplayName());
rule.ValidationParameters.Add("dependentname", DependentName);
rule.ValidationParameters.Add("dependentvalue", DependentValue);
rule.ValidationType = "requiredif";
yield return rule;
}
你的js代码需要进行一些修改。我不是一个不显眼的大师,但它对我有用:
if ($.validator && $.validator.unobtrusive) {
$.validator.unobtrusive.adapters.add('requiredif', ['dependentname', 'dependentvalue'], function(options) {
options.rules["requiredif"] = options.params;
options.messages["requiredif"] = options.message;
});
$.validator.addMethod('requiredif', function(value, element, params) {
var dependentElement = $("input[name=" + params["dependentname"] + "]");
var dependentvalue = params["dependentvalue"];
var actualvalue = dependentElement.parents(".dxWeb_edtCheckBoxChecked_DevEx:first").length > 0; //is checkbox is checked
if (dependentvalue == "True" && actualvalue && (value == null || value.length <= 0)) {
return false;
}
return true;
});
}
此代码仅适用于此示例。它假设RequiresAddress
propery是一个bool并且它呈现为chckbox。它需要更多的编码才能适用于所有类型。我知道,为另一种类型编写另一个验证器会更好。
这对你来说是个好方法吗?
顺便说一句。 RequiredIfAttribute
构造函数有三个参数,在Student
类的例子中有两个传递;)