[英]Get full HTML field name for client side validation in ASP.NET Core
我正在實現一個自定義驗證屬性。 此屬性不僅查看它所應用的屬性的值,還查看另一個屬性的值。 另一個屬性由其名稱指定。
我需要找到一種方法來獲取其他屬性的輸入將在最終 HTML output 中具有的完整 ID。
這是我的驗證屬性的簡化版本:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
public class MyCustomValidationAttribute : ValidationAttribute, IClientModelValidator
{
private string _otherPropertyName;
public MyCustomValidationAttribute(string otherPropertyName)
{
_otherPropertyName = otherPropertyName;
}
protected override ValidationResult IsValid(object value, ValidationContext context)
{
var otherProperty = context.ObjectInstance.GetType().GetProperty(_otherPropertyName);
var otherPropertyValue = Convert.ToString(otherProperty.GetValue(context.ObjectInstance, null));
// Validation logic...
}
public void AddValidation(ClientModelValidationContext context)
{
MergeAttribute(context.Attributes, "data-val", "true");
var errorMessage = FormatErrorMessage(context.ModelMetadata.GetDisplayName());
MergeAttribute(context.Attributes, "data-val-mycustomvalidation", errorMessage);
// THIS ROW NEEDS TO BE FIXED
MergeAttribute(context.Attributes, "data-val-mycustomvalidation-otherpropertyname", _otherProperyName);
}
private void MergeAttribute(IDictionary<string, string> attributes, string key, string value)
{
if (!attributes.ContainsKey(key))
{
attributes.Add(key, value);
}
}
}
這演示了如何在 model class 中使用它:
public class Report
{
[MyCustomValidation("Value2", ErrorMessage = "Error...")]
public string Value1 { get; set; }
public string Value2 { get; set; }
}
這是 JavaScript 以確保驗證也在客戶端完成:
$.validator.addMethod('mycustomvalidation',
function (value, element, parameters) {
var otherPropertyValue = $('#' + parameters.otherpropertyname).val();
// Validation logic...
});
$.validator.unobtrusive.adapters.add('mycustomvalidation', ['otherpropertyname'],
function (options) {
options.rules.mycustomvalidation = options.params;
options.messages['mycustomvalidation'] = options.message;
});
我的帶有表單的頁面/視圖的視圖模型如下所示:
public MyViewModel
{
public Report MyReport { get; set; }
}
請注意,我沒有使用 Report 作為我的視圖模型,而是作為視圖模型中屬性的類型。 這很重要,因為這是我問題的根源......
視圖中顯示 Value1 輸入的代碼並不奇怪(我使用的是 Razor 頁面):
<div>
<label asp-for="MyReport.Value1"></label>
<input asp-for="MyReport.Value1" />
<span asp-validation-for="MyReport.Value1"></span>
</div>
output 變為:
<label for="MyReport_Value1">Value1</label>
<input
type="text"
id="MyReport_Value1"
name="MyReport.Value1"
data-val="true"
data-val-mycustomvalidation="Error..."
data-val-mycustomvalidation-otherpropertyname="Value2"
value=""
>
<span
data-valmsg-for="MyReport.Value1"
data-valmsg-replace="true"
class="text-danger field-validation-valid"
></span>
所以問題是在 HTML output我需要 data-val-mycustomvalidation-otherpropertyname 是 "MyReport_Value2" 而不是 "Value2" 。 否則,驗證代碼將無法找到第二個 HTML 輸入(帶有 id MyReport_Value2)並執行驗證。
我認為這必須在屬性 class 中的方法 AddValidation() 中完成,但是如何獲得 HTML 輸入將收到的全名?
我猜有一些方法可以通過使用 context 參數來實現。 我見過類似“*.TemplateInfo.GetFullHtmlFieldId(PropertyName)”的例子,但我無法讓它工作。
任何幫助表示贊賞!
您將Value2
傳遞給MyCustomValidationAttribute
並使用Value2
設置_otherPropertyName
,然后使用
MergeAttribute(context.Attributes, "data-val-mycustomvalidation-otherpropertyname", _otherProperyName);
這樣 html 就會是
data-val-mycustomvalidation-otherpropertyname="Value2"
您只需要將Report_Value2
傳遞給MyCustomValidationAttribute
而不是Value2
。
public class Report
{
[MyCustomValidation("Report_Value2", ErrorMessage = "Error...")]
public string Value1 { get; set; }
public string Value2 { get; set; }
}
這樣你就會得到data-val-mycustomvalidation-otherpropertyname="Report_Value2"
ValidationContext 綁定到屬於驗證屬性的實例,即模型。 因此定位 ViewModel 的引用看起來很困難。 我可以提供三種不同的解決方案,您可以使用哪一種適合您的要求。
解決方案1:
使用 ValidationContext 您可以獲得屬性所屬的類的名稱。 只有當 ViewModel 屬性名稱必須與模型類名稱相同時,此解決方案才有效。 例如,如果模型類是學生,那么屬性名稱必須是學生。 如果屬性名稱是 Student1,它將不起作用。 即使類名和屬性名不同,解決方案 2 和 3 也能工作。
模型
public class Student
{
[Key]
public int Id { get; set; }
[Required(ErrorMessage = "Please enter name")]
public string Name { get; set; }
[Required]
[Country("Name")]
public string Country { get; set; }
}
視圖模型
public class StudentViewModel
{
public Student Student {get;set;} //Solution 1 wil not work for Student1
}
驗證屬性
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
public class CountryAttribute : ValidationAttribute, IClientModelValidator
{
private string _otherPropertyName;
private string _clientPropertyName;
public CountryAttribute(string otherPropertyName)
{
_otherPropertyName = otherPropertyName;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var otherProperty = validationContext.ObjectInstance.GetType().GetProperty(_otherPropertyName);
var otherPropertyValue = Convert.ToString(otherProperty.GetValue(validationContext.ObjectInstance, null));
_clientPropertyName = otherProperty.DeclaringType.Name +"_"+ otherProperty.Name;
}
public void AddValidation(ClientModelValidationContext context)
{
context.Attributes.Add("data-val", "true");
context.Attributes.Add("data-val-mycustomvalidation-otherpropertyname", _clientPropertyName);
}
}
解決方案2:
使用 ClientModelValidationContext 您可以獲得從控制器傳遞到視圖的 ViewModel 引用。 通過使用反射,我們可以獲得屬性的名稱,即模型。 要使用解決方案,您需要從控制器傳遞空的 ViewModel 引用。
控制器
public IActionResult New()
{
StudentViewModel studentViewModel = new StudentViewModel();
return View(studentViewModel);
}
驗證屬性
public void AddValidation(ClientModelValidationContext context)
{
var otherClientPropName = context.ModelMetadata.ContainerMetadata.Properties
.Single(p => p.PropertyName == this._otherPropertyName)
.GetDisplayName();
var viewContext = context.ActionContext as ViewContext;
if (viewContext?.ViewData.Model is StudentViewModel)
{
var model = (StudentViewModel)viewContext?.ViewData.Model;
var instanceName = model.GetType().GetProperties()[0].Name;
otherClientPropName = instanceName + "_" + otherClientPropName;
}
context.Attributes.Add("data-val", "true");
context.Attributes.Add("data-val-mycustomvalidation-otherpropertyname", otherClientPropName);
}
解決方案3:
使用 context.Attributes["id"] 您可以獲取當前屬性 id 值作為 string 。 通過使用字符串操作,您可以獲得前綴,然后您可以與其他屬性名稱合並。 此解決方案不需要來自控制器的空 ViewModel 引用。
控制器
public IActionResult New()
{
return View();
}
驗證屬性
public void AddValidation(ClientModelValidationContext context)
{
var otherClientPropName = context.ModelMetadata.ContainerMetadata.Properties
.Single(p => p.PropertyName == this._otherPropertyName)
.GetDisplayName();
var id = context.Attributes["id"];
var idPrefix = id.Split("_");
if (idPrefix.Length > 1)
{
otherClientPropName = idPrefix[0] + "_" + otherClientPropName;
}
context.Attributes.Add("data-val", "true");
context.Attributes.Add("data-val-mycustomvalidation-otherpropertyname", otherClientPropName);
}
HTML 輸出
<input class="form-control" type="text" data-val="true" data-val-required="Please enter name" id="Student_Name" name="Student.Name" value="">
<input class="form-control input-validation-error" type="text" data-val="true" data-val-mycustomvalidation-otherpropertyname="Student_Name" data-val-required="The Country field is required." id="Student_Country" name="Student.Country" value="">
當呈現的字段是 model 的較深子級時,這種方法也有效。
//Build the client id of the property name.
var dependentClientId = dependentPropertyName;
var clientId = context.Attributes["id"];
var clientIdArr = clientId.Split("_");
if (clientIdArr.Length > 1)
{
//Replace the last value of the array with the dependent property name.
clientIdArr[clientIdArr.Length - 1] = dependentPropertyName;
dependentClientId = string.Join("_", clientIdArr);
}
MergeAttribute(context.Attributes, "data-val-mycustomvalidation-otherpropertyname", dependentClientId );
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.