【发布时间】:2020-01-31 05:04:31
【问题描述】:
我有两个输入需要相互验证。它们是最小值和最大值。这是我定义它们的部分观点。
<div class="form-group">
<label asp-for="MinTubes" class="control-label"></label>
<input asp-for="MinTubes" class="form-control" />
<span asp-validation-for="MinTubes" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="MaxTubes" class="control-label"></label>
<input asp-for="MaxTubes" class="form-control" />
<span asp-validation-for="MaxTubes" class="text-danger"></span>
</div>
下面是它们映射到的属性:
[Display(Name = "Min Tubes")]
[MinToValidation("MaxTubes")]
public int MinTubes { get; set; }
[Display(Name = "Max Tubes")]
[MaxToValidation("MinTubes")]
public int MaxTubes { get; set; }
这会产生带有标签和验证消息的 <input type='number'> 元素。
我创建了两个自定义验证属性来使用它们。我只会发布 MaxToValidationAttribute 类,因为另一个在功能上是相同的。
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using System;
using System.ComponentModel.DataAnnotations;
namespace CCM_Util.CustomAttributes
{
public class MaxToValidationAttribute : ValidationAttribute, IClientModelValidator
{
public MaxToValidationAttribute(string min)
: base("{0} must be greater than or equal to {1}")
{
Min = min;
}
public string Min { get; set; }
public string FormatErrorMessage(string name, string minName)
{
return string.Format(ErrorMessageString, name, minName);
}
protected override ValidationResult
IsValid(object firstValue, ValidationContext validationContext)
{
var firstComparable = firstValue as IComparable;
var secondComparable = GetSecondComparable(validationContext);
if (firstComparable != null && secondComparable != null)
{
if (firstComparable.CompareTo(secondComparable) < 0)
{
object obj = validationContext.ObjectInstance;
var thing = obj.GetType().GetProperty(Min);
var displayName = (DisplayAttribute)Attribute.GetCustomAttribute(thing, typeof(DisplayAttribute));
return new ValidationResult(
FormatErrorMessage(validationContext.DisplayName, displayName.GetName()));
}
}
return ValidationResult.Success;
}
protected IComparable GetSecondComparable(
ValidationContext validationContext)
{
var propertyInfo = validationContext
.ObjectType
.GetProperty(Min);
if (propertyInfo != null)
{
var secondValue = propertyInfo.GetValue(
validationContext.ObjectInstance, null);
return secondValue as IComparable;
}
return null;
}
public void AddValidation(ClientModelValidationContext context)
{
context.Attributes.Add("data-val-min", Min);
context.Attributes.Add("data-val-ismax", "true");
}
}
}
然后,在 Default.js 中,我将以下函数作为 document.ready 函数的一部分运行。
function minMaxValidate() {
$("input[data-val-ismin='true']").each(function (i, ele) {
$(ele).change(function () {
var maxName = $(this).attr("data-val-max");
var minName = $(this).attr("name");
var minValue = parseFloat($(this).val());
var max = $("input[data-val-ismax='true'][name='" + maxName + "']");
var maxValue = max.val();
if (maxValue == "") { return }
maxValue = parseFloat(maxValue);
var validationMessage = $("span[data-valmsg-for='" + $(this).attr("name") + "']");
if (minValue > maxValue) {
validationMessage.html(minName + " must not be greater than " + maxName);
makeError(validationMessage);
}
else {
validationMessage.html("");
makeValid(validationMessage);
}
});
});
$("input[data-val-ismax='true']").each(function (i, ele) {
$(ele).change(function () {
var minName = $(this).attr("data-val-min");
var maxName = $(this).attr("name");
var maxValue = parseFloat($(this).val());
var min = $("input[data-val-ismin='true'][name='" + minName + "']");
var minValue = min.val();
if (minValue == "") { return }
minValue = parseFloat(minValue);
var validationMessage = $("span[data-valmsg-for='" + $(this).attr("name") + "']");
if (minValue > maxValue) {
validationMessage.html(maxName + " must not be less than " + minName);
makeError(validationMessage);
}
else {
validationMessage.html("");
makeValid(validationMessage);
}
});
});
}
makeError 和makeValid 函数本质上只是将validationMessage 的类分别更改为field-validation-error 和field-validation-valid,它们管理提交处理程序。
我知道这种类型的验证设置是有效的,因为我在其他地方使用它没有问题。
我已经逐步完成了更改处理程序,它们正在按应有的方式工作。但是,在错误消息出现并且代码退出后,它又消失了。我的猜测是 .NET 的默认数字输入验证正在接管,并且由于输入都是有效数字,因此它会删除错误消息。
有什么方法可以禁用 .NET 对数字类型输入的默认验证,这样我就可以自己处理它,而不会让外部黑盒代码弄乱我的东西?
谢谢。
编辑:
这里是完整的 view 和 Default.js 文件,以防万一。
@model Coils.CoilParts.Distributor
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@{
ViewData["Title"] = ViewData["ProgramName"];
}
<h1>Create</h1>
<h4>Distributor</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Name" class="control-label"></label>
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Material" class="control-label"></label>
@Html.DropDownListFor(m => m.Material.Key, ((IEnumerable<SelectListItem>)ViewData["Materials"]))
<span asp-validation-for="Material" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Type" class="control-label"></label>
<input asp-for="Type" class="form-control" />
<span asp-validation-for="Type" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="MinTubes" class="control-label"></label>
<input asp-for="MinTubes" class="form-control" />
<span asp-validation-for="MinTubes" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="MaxTubes" class="control-label"></label>
<input asp-for="MaxTubes" class="form-control" />
<span asp-validation-for="MaxTubes" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="InletSize" class="control-label"></label>
<input asp-for="InletSize" class="form-control" />
<span asp-validation-for="InletSize" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="PartNumber" class="control-label"></label>
<input asp-for="PartNumber" class="form-control" />
<span asp-validation-for="PartNumber" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Price" class="control-label"></label>
<input asp-for="Price" class="form-control" />
<span asp-validation-for="Price" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Weight" class="control-label"></label>
<input asp-for="Weight" class="form-control" />
<span asp-validation-for="Weight" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
$(document).ready(function () {
breadcrumbs();
measurementsValidate();
minMaxValidate();
});
var cgi_menus = {
'act': "the Accounting Menu",
'adm': "the Administation Menu",
'car': "the Corrective Action Request Menu",
'cprA': "the A+Pro Menu",
'cprC': "the CoilPro Menu",
'crm': "the Customer Relations Menu",
'dms': "the Document Management Menu",
'eng': "the Engineering Menu",
'etb': "the Engineering Toolbox Menu",
'ldc': "the Refrigeration Load Simulator Menu",
'mgt': "the Management Menu",
'prd': "the Production Menu",
'pur': "the Purchasing Menu",
'qcd': "the Quality Control Menu",
'rpt': "the Report Menu",
'sls': "the Sales Menu",
'sup': "the Administrator's Menu",
'usr': "the User Management Menu",
'utl': "the Utility Menu",
};
function breadcrumbs() {
var tab = {};
localStorage[curURL] = curProgram;
if (sessionStorage.tab) {
tab = JSON.parse(sessionStorage.tab);
}
else {
tab.cur_url = "";
tab.prev_url = "";
tab.history = [];
}
// handle coming from an existing Perl CGI menu
if (!tab.prev_url && document.referrer) {
var menu_info = document.referrer.match(/cgi-s[\\/](\w{3})[\\/]menu(\w?)\.cgi/);
var referrer = new URL(document.referrer);
if (menu_info && referrer.hostname === document.location.hostname) {
tab.cur_url = document.referrer;
var cgi_area = menu_info[1];
if (cgi_area == "cpr") { cgi_area += menu_info[2]; }
localStorage[tab.cur_url] = cgi_menus[cgi_area];
}
}
tab.time = Date.now();
tab.history.push(tab.prev_url);
tab.prev_url = tab.cur_url;
tab.cur_url = curURL;
if (tab.prev_url == tab.cur_url) {
tab.prev_url = tab.history.pop();
}
else if (tab.cur_url == tab.history[tab.history.length - 1]) {
tab.history.pop();
tab.prev_url = tab.history.pop();
}
if (tab.prev_url) {
$("#breadcrumbs").css("display", "inline-block");
$("#breadcrumbs").attr("href", tab.prev_url);
$("#breadcrumbs").html("< Back to " + localStorage[tab.prev_url]);
}
sessionStorage.tab = JSON.stringify(tab);
}
function minMaxValidate() {
$("input[data-val-ismin='true']").each(function (i, ele) {
$(ele).change(function () {
var maxName = $(this).attr("data-val-max");
var minName = $(this).attr("name");
var minValue = parseFloat($(this).val());
var max = $("input[data-val-ismax='true'][name='" + maxName + "']");
var maxValue = max.val();
if (maxValue == "") { return }
maxValue = parseFloat(maxValue);
var validationMessage = $("span[data-valmsg-for='" + $(this).attr("name") + "']");
if (minValue > maxValue) {
validationMessage.html(minName + " must not be greater than " + maxName);
makeError(validationMessage);
}
else {
validationMessage.html("");
makeValid(validationMessage);
}
});
});
$("input[data-val-ismax='true']").each(function (i, ele) {
$(ele).change(function () {
var minName = $(this).attr("data-val-min");
var maxName = $(this).attr("name");
var maxValue = parseFloat($(this).val());
var min = $("input[data-val-ismin='true'][name='" + minName + "']");
var minValue = min.val();
if (minValue == "") { return }
minValue = parseFloat(minValue);
var validationMessage = $("span[data-valmsg-for='" + $(this).attr("name") + "']");
if (minValue > maxValue) {
validationMessage.html(maxName + " must not be less than " + minName);
makeError(validationMessage);
}
else {
validationMessage.html("");
makeValid(validationMessage);
}
});
});
}
function measurementsValidate() {
$("input[data-val-measurement='true']").each(function (i, ele) {
var enforceUnits = $(ele).attr("data-val-units");
var units = "";
var hasMax = $(ele).attr("data-val-max");
var max = 0;
var hasMin = $(ele).attr("data-val-min");
var min = 0;
var value = $(ele).val();
var validationRegex = /[0-9/.]+ [a-z./\^0-9*()]+/i;
var name = $("label[for='" + $(ele).attr("name") + "']").html();
var validationMessage = $("span[data-valmsg-for='" + $(ele).attr("name") + "']");
if (typeof (enforceUnits) !== undefined && typeof (enforceUnits) !== false) {
enforceUnits = true;
units = $(ele).attr("data-val-units");
if (hasMax != null && hasMax != false) {
hasMax = true;
max = $(ele).attr("data-val-max");
}
if (hasMin != null && hasMin != false) {
hasMin = true;
min = $(ele).attr("data-val-min");
}
$(ele).change(function () {
var value = $(this).val(); // don't know why this has to be re-evaluated, but it does
if (!value.match(validationRegex)) {
validationMessage.html(name + " must be a valid Measurement. (example: 12 in)");
makeError(validationMessage);
}
else {
$.post("/Validations/Measurements", { EnforceUnits: enforceUnits, Units: units, HasMax: hasMax, Max: max, HasMin: hasMin, Min: min, Value: value })
.done(function (result) {
if (result == "true") {
validationMessage.html("");
makeValid(validationMessage);
}
else {
validationMessage.html(name + result);
makeError(validationMessage);
}
})
.fail(function () {
validationMessage.html("");
makeValid(validationMessage);
});
}
});
}
else {
$(ele).change(function () {
if (!value.match(validationRegex)) {
validationMessage.html(name + " must be a valid Measurement. (example: 12 in)");
makeError(validationMessage);
}
else {
validationMessage.html("");
makeValid(validationMessage);
}
});
}
});
}
function makeError(ele) {
$(ele).removeClass("field-validation-valid");
$(ele).addClass("field-validation-error");
$(ele).closest("form").unbind("submit");
$(ele).closest("form").submit(function () { return false });
}
function makeValid(ele) {
$(ele).removeClass("field-validation-error");
$(ele).addClass("field-validation-valid");
$(ele).closest("form").unbind("submit");
$(ele).closest("form").submit(checkFormValidation);
}
function checkFormValidation() {
if ($(this).find(".field-validation-error")[0]) {
return false;
}
return true;
}
编辑 2:
这是正在发生的事情的 GIF:https://i.imgur.com/eAiqxQJ.mp4
【问题讨论】:
-
“出现错误信息,代码退出后,又消失了”是什么意思。我做了一个演示来测试你的代码,但似乎没有问题。您能否分享更多可以重现该问题的 jQuery 文件和查看文件的详细信息?
-
@XueliChen 当我单步执行 javascript 时,它显示错误消息就好了,但是一旦更改处理程序退出,错误消息就会再次消失。当我检查元素时,该类已被重置为有效。我最好的猜测是默认数字输入验证覆盖了我自己的代码。我将编辑问题以包含整个视图文件和 Default.js 文件,但我不确定它们有什么相关性。
标签: c# jquery validation asp.net-core