如何获得模型属性的id,以便在MVC3中使用自定义IClientValidatable
本文关键字:MVC3 IClientValidatable 自定义 模型 何获得 属性 id | 更新日期: 2023-09-27 18:04:29
我正在尝试编写一个自定义验证属性,该属性将根据模型的布尔属性有条件地要求字段。
我有我的属性实现IClientValidatable。我有要检查的属性的名称,但我不知道如何获得目标属性的客户端id。
public IEnumerable<ModelClientValidationRule>
GetClientValidationRules(ModelMetadata metadata,
ControllerContext context)
{
var clientTarget = ?????;
var rule = new ModelClientValidationRule()
{
ErrorMessage =
FormatErrorMessage(metadata.DisplayName ?? metadata.PropertyName),
ValidationType = "requiredif"
};
rule.ValidationParameters["target"] = clientTarget;
yield return rule;
}
javascript: $.validator.addMethod("requiredif", function (value, element, target)
{
//check on value of target
});
$.validator.unobtrusive.adapters.addSingleVal("requiredif", "target");
我如何获得目标属性的客户端id,以便客户端javascript可以检查值?
我采用了Nathan的出色答案,添加了一些注释,并将其包装在名为GetHtmlId的扩展方法中,因此现在我可以使用这样的代码来获取同一页面上任何其他元素的HTML ID:
public virtual IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule {
ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
ValidationType = "requiredif",
};
// Find the value on the control we depend on...
string depProp = this.GetHtmlId(metadata, context, this.DependentPropertyName);
rule.ValidationParameters.Add("dependentproperty", depProp);
yield return rule;
}
这里是扩展方法:
using System;
using System.Collections.Generic;
using System.Web.Mvc;
namespace sbs.Lib.Web.ValidationAttributes
{
public static class IClientValidatableExtensions
{
/// <summary> Returns the HTML ID of the specified view model property. </summary>
/// <remarks> Based on: http://stackoverflow.com/a/21018963/1637105 </remarks>
/// <param name="metadata"> The model metadata. </param>
/// <param name="viewContext"> The view context. </param>
/// <param name="propertyName"> The name of the view model property whose HTML ID is to be returned. </param>
public static string GetHtmlId(this IClientValidatable me,
ModelMetadata metadata, ControllerContext context,
string propertyName)
{
var viewContext = context as ViewContext;
if (viewContext == null || viewContext.ViewData.TemplateInfo.HtmlFieldPrefix == string.Empty) {
return propertyName;
} else {
// This is tricky. The "Field ID" returned by GetFullHtmlFieldId is the HTML ID
// attribute created by the MVC view engine for the property whose validator is
// being set up by the caller of this routine. This code removes the property
// name from the Field ID, then inserts the specified property name.
// Of course, this only works for elements on the same page as the caller of
// this routine!
string fieldId = viewContext.ViewData.TemplateInfo.GetFullHtmlFieldId("");
fieldId = fieldId.Remove(fieldId.LastIndexOf("_"));
return fieldId + "_" + propertyName;
}
}
}
}
这只测试了MVC5,但我怀疑它没有从MVC3改变。
这是有点丑,但它似乎工作。这里有两个假设:
- MVC框架实际上总是为
GetClientValidationRules(....)
的ControllerContext
参数传递ViewContext
。在我所有的测试中都是这样,但我不能保证100%。 - 另一个属性位于相同的模型类级别(例如,不是复杂类型的子属性)
如果这两个假设都成立,那么以下似乎成立:
var viewContext = (ViewContext)context;
if(ViewData.TemplateInfo.HtmlFieldPrefix != string.Empty)
{
string fieldId = viewContext.ViewData.TemplateInfo.GetFullHtmlFieldId("");
fieldId = fieldId.Remove(fieldId.LastIndexOf("_"));
fieldId = fieldId + "_" + BooleanPropertyName
}
else
{
string fieldId = BooleanPropertyName
}
这适合我,可能需要一点调整,但你可以得到的想法:
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule()
{
ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
ValidationType = "requiredif",
};
string depProp = BuildDependentPropertyId(metadata, context as ViewContext);
// find the value on the control we depend on;
// if it's a bool, format it javascript style
// (the default is True or False!)
string targetValue = (this.TargetValue ?? "").ToString();
if (this.TargetValue != null && this.TargetValue.GetType() == typeof(bool))
targetValue = targetValue.ToLower();
rule.ValidationParameters.Add("dependentproperty", depProp);
rule.ValidationParameters.Add("targetvalue", targetValue);
yield return rule;
}
private string BuildDependentPropertyId(ModelMetadata metadata, ViewContext viewContext)
{
return QualifyFieldId(metadata, this.DependentProperty, viewContext);
}
设置如下属性:
[RequiredIf("SelectedPeriod", "DateRange", ErrorMessageResourceName = "FromDateRequired", ErrorMessageResourceType = typeof(Common))]
public DateTime? StartDate { get; set; }
//dependent property
public string SelectedPeriod { get; set; }
下面是获取字段id的方法:
protected string QualifyFieldId(ModelMetadata metadata, string fieldId, ViewContext viewContext)
{
// build the ID of the property
string depProp = viewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(fieldId);
var thisField = metadata.PropertyName + "_";
if (depProp.StartsWith(thisField))
// strip it off again
depProp = depProp.Substring(thisField.Length);
else if (null != metadata.ContainerType && !string.IsNullOrEmpty(metadata.ContainerType.Name))
{
depProp = metadata.ContainerType.Name + "_" + fieldId;
}
return depProp;
}
看一下这篇文章。它讨论了服务器端的DataAnnotation验证,并演示了如何通过在客户端实现IClientVaildatable和编写一些jquery来在客户端hook这些属性。