MVC 5: проверка двух свойств DateTime, введенных в виде отдельных полей - день, месяц, год - PullRequest
0 голосов
/ 03 июля 2018

Цель высшего уровня : мне нужно рассчитать возраст человека, введя 2 даты - дату рождения и дату смерти.

Сведения о цели : эти даты следует вводить в виде отдельных полей - день, месяц, год (как показано на рисунке - способ ввода двух полей DateTime ). Эти поля должны быть проверены для диапазона (день - 1..31, месяц - 1..12, год - 1900 ... ток) и для отношения - дата смерти должна быть больше даты рождения.

Вопрос : Как проверить соотношение двух дат, отобразить сообщения об ошибках, если я не создаю Editor для поля DateOfDeath3 (Date3GreaterThanAnother атрибут проверки применяется к этому полю)? Мне нужно подтвердить дату, но я отображаю только ее части - день, месяц, год.

Может быть, я выбрал неправильный подход для всего этого, пожалуйста, дайте мне подсказку, как это можно сделать лучше ...

Спасибо!

Сведения о текущей реализации : я уже реализовал ввод, сохранение, проверку дат (только на стороне сервера).

Для реализации ввода DateTime в виде отдельных полей я использую следующий класс:

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 используется в модели представления, имеющей следующий код:

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 выглядит следующим образом (я пытался реализовать проверку на стороне клиента, поэтому он реализует интерфейс 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 - я нашел решения для стекового потока в отношении ненавязчивой проверки JQuery, но не реализовал его, поскольку не могу запустить его из-за проблем, связанных с MVC.

Без использования DateOfDeath3 на странице просмотра ненавязчивая проверка не работает. Я пытался добавить HiddenFor для DateOfDeath3, ненавязчивая проверка jquery начала работать, но в классе Date3GreaterThanAnother я обнаружил исключения для значение параметр = ноль.

$.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;
    });

1 Ответ

0 голосов
/ 03 июля 2018

Рассмотрим следующий подход:

Вот ваша модель:

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, и все элементы управления отображаются автоматически

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...