MVC 5:验证作为单独字段输入的两个 DateTime 属性 - 日、月、年
MVC 5: Validation of two DateTime properties entered as separate fields - day, month, year
顶级目标:我需要通过输入 2 个日期 - 出生日期和死亡日期来计算一个人的年龄。
目标详细信息:这些日期应作为单独的字段输入 - 日、月、年(如图所示 - The way I enter two DateTime fields)。
应验证这些字段的范围(日 - 1..31,月 - 1..12,年 - 1900..当前)和关系 - 死亡日期应大于出生日期。
问题:如果我不为 DateOfDeath3 字段创建 EditorFor(Date3GreaterThanAnother 验证属性应用于此字段),如何验证 2 个日期的关系并显示错误消息?我需要验证日期,但我只显示它的部分 - 日、月、年。
也许我选择了错误的方法来做这一切,请给我一个提示,告诉我如何才能做得更好...
谢谢!
当前实现细节:我已经实现了日期输入、保存、验证(仅限服务器端)。
为了将 DateTime 输入作为单独的字段,我使用以下 class:
public class Date3
{
/// <summary>
/// Creates Date3 instance with internal DateTime object set to null.
/// </summary>
public Date3()
{
DateTime = null;
}
public Date3(DateTime dateTime)
{
DateTime = dateTime;
Day = dateTime.Day;
Month = dateTime.Month;
Year = dateTime.Year;
}
public DateTime? DateTime { get; private set; }
/// <summary>
/// Recreates inner DateTime object with specified Day, Month and Year properties.
/// </summary>
public void UpdateInner()
{
if (Day.HasValue && Month.HasValue && Year.HasValue)
DateTime = new DateTime(Year.Value, Month.Value, Day.Value);
}
public override string ToString()
{
return DateTime.ToString();
}
[Range(1, 31, ErrorMessage = "Day number should be from 1 to 31.")]
public int? Day { get; set; }
[Range(1, 12, ErrorMessage = "Month number should be from 1 to 12.")]
public int? Month { get; set; }
[RangeCurrentYear(1900, ErrorMessage = "Year number should be from 1900 to current year.")]
public int? Year { get; set; }
}
DateTime 的 "wrapper" 用于具有以下代码的视图模型中:
public class ViewModel
{
...
public Date3 DateOfBirth3 { get; set; }
[Date3GreaterThanAnother("DateOfBirth3", "Date of death should be after date of birth.")]
public Date3 DateOfDeath3 { get; set; }
...
}
Date3GreaterThanAnother 验证属性 class 看起来像这样(我尝试实现客户端验证,这就是它实现 IClientValidatable 接口的原因):
[AttributeUsage(AttributeTargets.Property)]
public class Date3GreaterThanAnotherAttribute : ValidationAttribute, IClientValidatable
{
private readonly string _anotherProperty;
public Date3GreaterThanAnotherAttribute(string anotherDatePropertyName, string errorMessage = null)
{
_anotherProperty = anotherDatePropertyName;
if(errorMessage != null)
ErrorMessage = errorMessage;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var property = validationContext.ObjectType.GetProperty(_anotherProperty);
if (property == null)
return new ValidationResult($"Unknown property {_anotherProperty}", new string[] {_anotherProperty});
var otherDateValue = property.GetValue(validationContext.ObjectInstance, null);
if (!(otherDateValue is Date3 otherDate3))
return new ValidationResult($"Other property is not of Date3 type.");
var date3 = (Date3)value;
otherDate3.UpdateInner();
date3.UpdateInner();
if (otherDate3.DateTime >= date3.DateTime)
return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
return ValidationResult.Success;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule()
{
ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
ValidationType = "greaterthananotherdate"
};
rule.ValidationParameters.Add("otherdate", _anotherProperty);
yield return rule;
}
}
最后,这是我在查看页面中显示它的方式(删除了不相关的标记代码):
...
@Html.LabelFor(x => x.Estate.DateOfBirth3.Day, "Date of Birth", new {@class = "control-label col-md-2 control-label-details"})
<div class="col-md-3">
@Html.EditorFor(x => x.Estate.DateOfBirth3.Day, new {htmlAttributes = new {@class = "form-control date3 day", placeholder = "DD"}})
@Html.ValidationMessageFor(x => x.Estate.DateOfBirth3.Day)
/
@Html.EditorFor(x => x.Estate.DateOfBirth3.Month, new {htmlAttributes = new {@class = "form-control date3 month", placeholder = "MM"}})
@Html.ValidationMessageFor(x => x.Estate.DateOfBirth3.Month)
/
@Html.EditorFor(x => x.Estate.DateOfBirth3.Year, new {htmlAttributes = new {@class = "form-control date3 year", placeholder = "YYYY"}})
@Html.ValidationMessageFor(x => x.Estate.DateOfBirth3.Year)
</div>
...
@Html.LabelFor(x => x.Estate.DateOfDeath3.Day, "Date of Death", new { @class = "control-label col-md-2 control-label-details" })
<div class="col-md-3">
@Html.EditorFor(x => x.Estate.DateOfDeath3.Day, new { htmlAttributes = new { @class = "form-control date3 day", placeholder = "DD" } })
@Html.ValidationMessageFor(x => x.Estate.DateOfDeath3.Day)
/
@Html.EditorFor(x => x.Estate.DateOfDeath3.Month, new { htmlAttributes = new { @class = "form-control date3 month", placeholder = "MM" } })
@Html.ValidationMessageFor(x => x.Estate.DateOfDeath3.Month)
/
@Html.EditorFor(x => x.Estate.DateOfDeath3.Year, new { htmlAttributes = new { @class = "form-control date3 year", placeholder = "YYYY" } })
@Html.ValidationMessageFor(x => x.Estate.DateOfDeath3.Year)
</div>
@Html.ValidationMessageFor(x => x.Estate.DateOfDeath3)
...
如果 JS 代码是必需的 - 我在 Whosebug 上找到了关于不显眼 JQuery 验证的解决方案,但没有实现它,因为我无法实现它 运行 因为我的 MVC 相关问题.
在查看页面中不使用任何 DateOfDeath3 不引人注目的验证不起作用。
我尝试为 DateOfDeath3 添加 HiddenFor,不显眼的 jquery 验证开始工作但在 Date3GreaterThanAnother class 中我捕获了 value 参数 = null.[=16= 的异常]
$.validator.addMethod("greaterthananotherdate",
function (value, element, params) {
// TODO: implement validation logic.
console.log("validation method triggered");
return false;
}, 'ERROR');
$.validator.unobtrusive.adapters.add(
"greaterthananotherdate",
["otherdate"],
function (options) {
console.log("adapter triggered");
options.rules["greaterthananotherdate"] = "#" + options.params.otherdate;
options.messages["greaterthananotherdate"] = options.message;
});
考虑以下方法:
这是您的模型:
public class AgeModel: IValidatableObject
{
[DataType(DataType.Date)]
public DateTime Birth { get; set; }
[DataType(DataType.Date)]
public DateTime Death { get; set; }
public TimeSpan Age
{
get
{
return this.Death.Subtract(this.Birth);
}
}
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (this.Death < this.Birth)
yield return new ValidationResult("It died before birth", new[] { nameof(this.Death) });
if (this.Birth > this.Death)
yield return new ValidationResult("It born after death", new[] { nameof(this.Birth) });
}
}
这是您的看法:
@using (Html.BeginForm("Age", "User", FormMethod.Post))
{
<br />
@Html.EditorFor(m => m.Birth)
@Html.ValidationMessageFor(m => m.Birth)
<br />
@Html.EditorFor(m => m.Death)
@Html.ValidationMessageFor(m => m.Death)
<br />
@Html.ValidationSummary();
<br />
<input type="submit" value="Submit" />
}
不需要对 DateTime 进行包装,所有控件都自动呈现
顶级目标:我需要通过输入 2 个日期 - 出生日期和死亡日期来计算一个人的年龄。
目标详细信息:这些日期应作为单独的字段输入 - 日、月、年(如图所示 - The way I enter two DateTime fields)。 应验证这些字段的范围(日 - 1..31,月 - 1..12,年 - 1900..当前)和关系 - 死亡日期应大于出生日期。
问题:如果我不为 DateOfDeath3 字段创建 EditorFor(Date3GreaterThanAnother 验证属性应用于此字段),如何验证 2 个日期的关系并显示错误消息?我需要验证日期,但我只显示它的部分 - 日、月、年。
也许我选择了错误的方法来做这一切,请给我一个提示,告诉我如何才能做得更好...
谢谢!
当前实现细节:我已经实现了日期输入、保存、验证(仅限服务器端)。
为了将 DateTime 输入作为单独的字段,我使用以下 class:
public class Date3
{
/// <summary>
/// Creates Date3 instance with internal DateTime object set to null.
/// </summary>
public Date3()
{
DateTime = null;
}
public Date3(DateTime dateTime)
{
DateTime = dateTime;
Day = dateTime.Day;
Month = dateTime.Month;
Year = dateTime.Year;
}
public DateTime? DateTime { get; private set; }
/// <summary>
/// Recreates inner DateTime object with specified Day, Month and Year properties.
/// </summary>
public void UpdateInner()
{
if (Day.HasValue && Month.HasValue && Year.HasValue)
DateTime = new DateTime(Year.Value, Month.Value, Day.Value);
}
public override string ToString()
{
return DateTime.ToString();
}
[Range(1, 31, ErrorMessage = "Day number should be from 1 to 31.")]
public int? Day { get; set; }
[Range(1, 12, ErrorMessage = "Month number should be from 1 to 12.")]
public int? Month { get; set; }
[RangeCurrentYear(1900, ErrorMessage = "Year number should be from 1900 to current year.")]
public int? Year { get; set; }
}
DateTime 的 "wrapper" 用于具有以下代码的视图模型中:
public class ViewModel
{
...
public Date3 DateOfBirth3 { get; set; }
[Date3GreaterThanAnother("DateOfBirth3", "Date of death should be after date of birth.")]
public Date3 DateOfDeath3 { get; set; }
...
}
Date3GreaterThanAnother 验证属性 class 看起来像这样(我尝试实现客户端验证,这就是它实现 IClientValidatable 接口的原因):
[AttributeUsage(AttributeTargets.Property)]
public class Date3GreaterThanAnotherAttribute : ValidationAttribute, IClientValidatable
{
private readonly string _anotherProperty;
public Date3GreaterThanAnotherAttribute(string anotherDatePropertyName, string errorMessage = null)
{
_anotherProperty = anotherDatePropertyName;
if(errorMessage != null)
ErrorMessage = errorMessage;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var property = validationContext.ObjectType.GetProperty(_anotherProperty);
if (property == null)
return new ValidationResult($"Unknown property {_anotherProperty}", new string[] {_anotherProperty});
var otherDateValue = property.GetValue(validationContext.ObjectInstance, null);
if (!(otherDateValue is Date3 otherDate3))
return new ValidationResult($"Other property is not of Date3 type.");
var date3 = (Date3)value;
otherDate3.UpdateInner();
date3.UpdateInner();
if (otherDate3.DateTime >= date3.DateTime)
return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
return ValidationResult.Success;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule()
{
ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
ValidationType = "greaterthananotherdate"
};
rule.ValidationParameters.Add("otherdate", _anotherProperty);
yield return rule;
}
}
最后,这是我在查看页面中显示它的方式(删除了不相关的标记代码):
...
@Html.LabelFor(x => x.Estate.DateOfBirth3.Day, "Date of Birth", new {@class = "control-label col-md-2 control-label-details"})
<div class="col-md-3">
@Html.EditorFor(x => x.Estate.DateOfBirth3.Day, new {htmlAttributes = new {@class = "form-control date3 day", placeholder = "DD"}})
@Html.ValidationMessageFor(x => x.Estate.DateOfBirth3.Day)
/
@Html.EditorFor(x => x.Estate.DateOfBirth3.Month, new {htmlAttributes = new {@class = "form-control date3 month", placeholder = "MM"}})
@Html.ValidationMessageFor(x => x.Estate.DateOfBirth3.Month)
/
@Html.EditorFor(x => x.Estate.DateOfBirth3.Year, new {htmlAttributes = new {@class = "form-control date3 year", placeholder = "YYYY"}})
@Html.ValidationMessageFor(x => x.Estate.DateOfBirth3.Year)
</div>
...
@Html.LabelFor(x => x.Estate.DateOfDeath3.Day, "Date of Death", new { @class = "control-label col-md-2 control-label-details" })
<div class="col-md-3">
@Html.EditorFor(x => x.Estate.DateOfDeath3.Day, new { htmlAttributes = new { @class = "form-control date3 day", placeholder = "DD" } })
@Html.ValidationMessageFor(x => x.Estate.DateOfDeath3.Day)
/
@Html.EditorFor(x => x.Estate.DateOfDeath3.Month, new { htmlAttributes = new { @class = "form-control date3 month", placeholder = "MM" } })
@Html.ValidationMessageFor(x => x.Estate.DateOfDeath3.Month)
/
@Html.EditorFor(x => x.Estate.DateOfDeath3.Year, new { htmlAttributes = new { @class = "form-control date3 year", placeholder = "YYYY" } })
@Html.ValidationMessageFor(x => x.Estate.DateOfDeath3.Year)
</div>
@Html.ValidationMessageFor(x => x.Estate.DateOfDeath3)
...
如果 JS 代码是必需的 - 我在 Whosebug 上找到了关于不显眼 JQuery 验证的解决方案,但没有实现它,因为我无法实现它 运行 因为我的 MVC 相关问题.
在查看页面中不使用任何 DateOfDeath3 不引人注目的验证不起作用。 我尝试为 DateOfDeath3 添加 HiddenFor,不显眼的 jquery 验证开始工作但在 Date3GreaterThanAnother class 中我捕获了 value 参数 = null.[=16= 的异常]
$.validator.addMethod("greaterthananotherdate",
function (value, element, params) {
// TODO: implement validation logic.
console.log("validation method triggered");
return false;
}, 'ERROR');
$.validator.unobtrusive.adapters.add(
"greaterthananotherdate",
["otherdate"],
function (options) {
console.log("adapter triggered");
options.rules["greaterthananotherdate"] = "#" + options.params.otherdate;
options.messages["greaterthananotherdate"] = options.message;
});
考虑以下方法:
这是您的模型:
public class AgeModel: IValidatableObject
{
[DataType(DataType.Date)]
public DateTime Birth { get; set; }
[DataType(DataType.Date)]
public DateTime Death { get; set; }
public TimeSpan Age
{
get
{
return this.Death.Subtract(this.Birth);
}
}
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (this.Death < this.Birth)
yield return new ValidationResult("It died before birth", new[] { nameof(this.Death) });
if (this.Birth > this.Death)
yield return new ValidationResult("It born after death", new[] { nameof(this.Birth) });
}
}
这是您的看法:
@using (Html.BeginForm("Age", "User", FormMethod.Post))
{
<br />
@Html.EditorFor(m => m.Birth)
@Html.ValidationMessageFor(m => m.Birth)
<br />
@Html.EditorFor(m => m.Death)
@Html.ValidationMessageFor(m => m.Death)
<br />
@Html.ValidationSummary();
<br />
<input type="submit" value="Submit" />
}
不需要对 DateTime 进行包装,所有控件都自动呈现