提问人:Chris Woodard 提问时间:6/1/2023 最后编辑:Chris Woodard 更新时间:6/2/2023 访问量:328
ASP.NET Core 6:具有客户端验证的自定义 vallidation 属性
ASP.NET Core 6 : custom vallidation attribute with client-side validation
问:
我有必须为其创建自定义验证属性的表单。我的自定义属性在服务器上起作用,但我不希望它转到服务器来验证这一点。我希望它在客户端工作。
此验证器所做的是查看为所述属性选择的内容。如果选择了该属性的值,则此属性是必需的。
该属性的应用方式如下:
[AnotherTest("CurrentlyPracticing", 1,
ErrorMessage = "Required if practicing.")]
public string? ProfLicense { get; set; } = string.Empty;
如您所见,仅当 .ProfLicense
CurrentlyPracticing = 1
下面是我的模型类、自定义验证属性代码和此项目的视图。
我的模型类(忽略注释掉的部分 - 这是一项正在进行的工作):
public class ApplicationVM
{
// other attributes removed because of Stackoverflow space requirements
[Required(ErrorMessage = "You must answer this question.")]
[Display(Name = "Are you a licensed medical practitioner?")]
public int? LicMedPract { get; set; }
[RequiredIfTrue(nameof(LicMedPract), ErrorMessage = "An answer is required.")]
[Display(Name = "Currently Practicing?")]
public int? CurrentlyPracticing { get; set; }
[RequiredIfTrue(nameof(CurrentlyPracticing), ErrorMessage = "You must select a Specialty.")]
[Display(Name = "Practice Specialty:")]
public int? SpecialtyID { get; set; }
[RequiredIfTrue(nameof(SpecialtyID), ErrorMessage = "You must type an Other Specialty.")]
[StringLength(25)]
[Display(Name = "Other Specialty:")]
public string PracSpecOther { get; set; } = string.Empty;
[Display(Name = "License Number")]
[AnotherTest("CurrentlyPracticing", 1,
ErrorMessage = "Required if practicing.")]
public string? ProfLicense { get; set; } = string.Empty;
[MaxLength(2)]
[Display(Name = "State Licensed")]
[AnotherTest("CurrentlyPracticing", 1,
ErrorMessage = "Required if practicing.")]
public string? ProfLicenseState { get; set; } = string.Empty;
}
这是我的自定义验证属性:
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using System.ComponentModel.DataAnnotations;
namespace pportal2.common.Validation
{
public class AnotherTestAttribute : ValidationAttribute, IClientModelValidator
{
private readonly string _propertyName;
private readonly object _desiredValue;
private readonly RequiredAttribute _required;
public AnotherTestAttribute(string propertyName, Object desiredValue)
{
_propertyName = propertyName;
_desiredValue = desiredValue;
_required = new RequiredAttribute();
}
public void AddValidation(ClientModelValidationContext context)
{
var errorMessage = FormatErrorMessage(context.ModelMetadata.GetDisplayName());
MergeAttribute(context.Attributes, "data-val", "true");
MergeAttribute(context.Attributes, "data-val-testif", errorMessage);
MergeAttribute(context.Attributes, "data-val-testif-propertyname", _propertyName);
MergeAttribute(context.Attributes, "data-val-testif-desiredvalue", _desiredValue.ToString());
}
protected override ValidationResult? IsValid(object? value, ValidationContext validationContext)
{
var dependentValue = validationContext.ObjectInstance.GetType().GetProperty(_propertyName).GetValue(validationContext.ObjectInstance, null);
if (dependentValue.ToString() == _desiredValue.ToString())
{
if (!_required.IsValid(value))
{
return new ValidationResult(ErrorMessage);
}
}
return ValidationResult.Success;
}
private static bool MergeAttribute(IDictionary<string, string> attributes, string key, string value)
{
if (attributes.ContainsKey(key))
{
return false;
}
attributes.Add(key, value);
return true;
}
}
}
我的观点(在这里我认为我的问题出在验证器部分。这是在脚本的底部):
<div class="col-md-8 m-auto">
<h1 class="text-center">PACE Program Initial Application</h1>
</div>
<hr />
<div class="col-md-8 m-auto">
<form asp-action="Application" id="application">
<hr />
</div>
<div class="form-group">
<h4>
<label>Professional Information:</label>
</h4>
<div class="form-group col-sm-12">
<div class="row">
<div class="form-group col-sm-6">
<label asp-for="LicMedPract" class="control-label"></label>
<select asp-for="LicMedPract" class="form-control" asp-items="ViewBag.YesNo2"></select>
<span asp-validation-for="LicMedPract" class="text-danger"></span>
</div>
</div>
<div id="divHiddenPracticeInfo" style="display:none;">
<div class="row">
<div class="form-group col-sm-5 col-md-4 col-lg-3">
<label asp-for="CurrentlyPracticing" class="control-label"></label>
<select asp-for="CurrentlyPracticing" class="form-control" asp-items="ViewBag.YesNo2"></select>
<span asp-validation-for="CurrentlyPracticing" class="text-danger"></span>
</div>
<div class=" col-sm-5 col-md-4 col-lg-3" id="divPracticeSpecialty" style="display: none;">
<div class="form-group col-md-12">
<label asp-for="SpecialtyID" class="control-label"></label>
<select asp-for="SpecialtyID" class="form-control" asp-items="ViewBag.Specialty"></select>
<span asp-validation-for="SpecialtyID" class="text-danger"></span>
</div>
</div>
<div class=" col-sm-5 col-md-4 col-lg-3" id="divPracSpecOther" style="display: none;">
<div class="form-group col-md-12">
<label asp-for="PracSpecOther" class="control-label"></label>
<input asp-for="PracSpecOther" class="form-control" />
<span asp-validation-for="PracSpecOther" class="text-danger"></span>
</div>
</div>
</div>
<div class="row">
<h5 class="col-md-12">Please include all letters and numbers.</h5>
<div class="form-group col-sm-5 col-md-4 col-lg-3">
<label asp-for="ProfLicense" class="control-label"></label>
<input asp-for="ProfLicense" class="form-control" />
<span asp-validation-for="ProfLicense" class="text-danger"></span>
</div>
<div class="form-group col-sm-5 col-md-4 col-lg-3">
<label asp-for="ProfLicenseState" class="control-label"></label>
<input asp-for="ProfLicenseState" class="form-control" />
<span asp-validation-for="ProfLicenseState" class="text-danger"></span>
</div>
</div>
<div class="row">
<div class="form-group col-sm-5 col-md-4 col-lg-3">
<label asp-for="DEA" class="control-label"></label>
<input asp-for="DEA" class="form-control" />
<span asp-validation-for="DEA" class="text-danger"></span>
</div>
</div>
</div>
</div>
</div>
<div asp-validation-summary="All" class="text-danger">
<span id="message2"></span>
</div>
<div class="form-group">
<input type="submit" value="Submit" class="btn btn-primary" />
</div>
<br />
</div>
</form>
</div>
@section Scripts {
@{
await Html.RenderPartialAsync("_ValidationScriptsPartial");
}
<script type="text/javascript">
// Other script code removed because of space limitations on stackoverflow....
$.validator.addMethod("testif", function (value, element, parameters) {
var practicing = $(parameters[0]).val(), desiredvalue = parameters[1], proflicense = value;
console.log(practicing[0].val());
if (practicing && practicing[0] === 1) {
alert(value);
return value != null;
}
});
$.validator.unobtrusive.adapters.add("testif", "desiredvalue", function (options) {
options.rules.testif = {};
options.
options.messages["testif"] = options.message;
});
</script>
}
答:
2赞
Qiang Fu
6/2/2023
#1
我写了一个小的工作示例,你可以尝试参考
ApplicationVM.cs
public class ApplicationVM
{
[Display(Name = "Currently Practicing?")]
public int? CurrentlyPracticing { get; set; }
[Display(Name = "State Licensed")]
[AnotherTest("CurrentlyPracticing", 1,ErrorMessage = "Required if practicing.")]
public string? ProfLicense { get; set; }
}
另一个测试属性.cs
public class AnotherTestAttribute : ValidationAttribute, IClientModelValidator
{
private readonly string _propertyName;
private readonly object _desiredValue;
public AnotherTestAttribute(string propertyName, object desiredValue)
{
_propertyName = propertyName;
_desiredValue = desiredValue;
}
public void AddValidation(ClientModelValidationContext context)
{
var errorMessage = FormatErrorMessage(context.ModelMetadata.GetDisplayName());
MergeAttribute(context.Attributes, "data-val", "true");
MergeAttribute(context.Attributes, "data-val-anothertest", errorMessage);
MergeAttribute(context.Attributes, "data-val-anothertest-propertyname", _propertyName);
MergeAttribute(context.Attributes, "data-val-anothertest-desiredvalue", _desiredValue.ToString());
}
private static bool MergeAttribute(IDictionary<string, string> attributes, string key, string value)
{
if (attributes.ContainsKey(key))
{
return false;
}
attributes.Add(key, value);
return true;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
//I didn't implement server validation
return ValidationResult.Success;
}
}
控制器
[HttpGet]
public IActionResult Index()
{
return View(new ApplicationVM());
}
[HttpPost]
public IActionResult Index(ApplicationVM applicationVM)
{
return View(applicationVM);
}
索引.cshtml
@model ApplicationVM
@{
List<SelectListItem> choices1 = new()
{
new SelectListItem { Value = "1",Text="Yes"},
new SelectListItem { Value = "0",Text="No"},
};
ViewBag.YesNo2 = choices1;
}
<div class="col-md-8 m-auto">
<h1 class="text-center">PACE Program Initial Application</h1>
</div>
<div class ="col-md-8 m-auto">@Model.ProfLicense</div>
<div class="col-md-8 m-auto">
<form asp-action="index" method="post">
<div class="form-group col-sm-5 col-md-4 col-lg-3">
<label asp-for="CurrentlyPracticing" class="control-label"></label>
<select asp-for="CurrentlyPracticing" class="form-control" id="CurrentlyPracticing" asp-items="ViewBag.YesNo2"></select>
<span asp-validation-for="CurrentlyPracticing" class="text-danger"></span>
</div>
<div class="form-group col-sm-5 col-md-4 col-lg-3">
<label asp-for="ProfLicense" class="control-label"></label>
<input asp-for="ProfLicense" class="form-control" />
<span asp-validation-for="ProfLicense" class="text-danger"></span>
</div>
<input type="submit" value="Submit" class="btn btn-primary" />
</form>
</div>
@section scripts {
<script src="~/lib/jquery-validation/dist/jquery.validate.min.js"></script>
<script src="~/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>
<script>
$.validator.addMethod("anothertest",
function (value, element, parameters) {
var propertyname = $(element).data('val-anothertest-propertyname');
var desiredvalue = $(element).data('val-anothertest-desiredvalue');
if ((value.trim() == "") && (document.getElementById(propertyname).value==desiredvalue)) {
return false
}
else{
return true
}
});
$.validator.unobtrusive.adapters.addBool("anothertest");
</script>
}
评论
1赞
Chris Woodard
6/2/2023
非常感谢傅强。我看到我只是不知道如何引用数据元素来完成这项工作。这太完美了,我真的很感谢你给我一个开始,并让我踏上我的自定义验证之旅。非常感谢我的朋友。
1赞
Chris Woodard
6/2/2023
PS - 现在我可以在我的项目中给它起真正有意义的名字并继续前进。我给它起了个名字叫AnotherTest,因为我太沮丧了。
评论