UFO ET IT

MVC 사용자 지정 유효성 검사 : 두 날짜 비교

ufoet 2020. 11. 28. 13:19
반응형

MVC 사용자 지정 유효성 검사 : 두 날짜 비교


두 날짜를 비교하고 두 번째 날짜가 첫 번째 날짜보다 큰지 확인하는 사용자 지정 ValidationAttribute를 만들었습니다.

public sealed class IsDateAfter : ValidationAttribute, IClientValidatable
{
    private readonly string testedPropertyName;
    private readonly bool allowEqualDates;

    public IsDateAfter(string testedPropertyName, bool allowEqualDates = false)
    {
        this.testedPropertyName = testedPropertyName;
        this.allowEqualDates = allowEqualDates;
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        var propertyTestedInfo = validationContext.ObjectType.GetProperty(this.testedPropertyName);
        if (propertyTestedInfo == null)
        {
            return new ValidationResult(string.Format("unknown property {0}", this.testedPropertyName));
        }

        var propertyTestedValue = propertyTestedInfo.GetValue(validationContext.ObjectInstance, null);

        if (value == null || !(value is DateTime))
        {
            return ValidationResult.Success;
        }

        if (propertyTestedValue == null || !(propertyTestedValue is DateTime))
        {
            return ValidationResult.Success;
        }

        // Compare values
        if ((DateTime)value >= (DateTime)propertyTestedValue)
        {
            if (this.allowEqualDates)
            {
                return ValidationResult.Success;
            }
            if ((DateTime)value > (DateTime)propertyTestedValue)
            {
                return ValidationResult.Success;
            }
        }

        return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
    }

    public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
    {
        var rule = new ModelClientValidationRule
        {
            ErrorMessage = this.ErrorMessageString,
            ValidationType = "isdateafter"
        };
        rule.ValidationParameters["propertytested"] = this.testedPropertyName;
        rule.ValidationParameters["allowequaldates"] = this.allowEqualDates;
        yield return rule;
    }

CalendarEntry 클래스 : ...

public virtual DateTime StartDate { get; set; }

[IsDateAfter("StartDate", true, ErrorMessage="End date needs to be after start date")]
public virtual DateTime EndDate { get; set; }

전망:

$.validator.unobtrusive.adapters.add(
    'isdateafter', ['propertytested', 'allowequaldates'], function (options) {
    options.rules['isdateafter'] = options.params;
    options.messages['isdateafter'] = options.message;
});
$.validator.addMethod("isdateafter", function(value, element, params) {
    alert(params.propertytested);
    var startdatevalue = $('input[name="' + params.propertytested + '"]').val();
    if (!value || !startdatevalue) return true;
    return (params.allowequaldates) ? Date.parse(startdatevalue) <= Date.parse(value) : Date.parse(startdatevalue) < Date.parse(value);
}, '');

CalendarEntry 가 다른 클래스 안에 래핑되지 않은 경우 제대로 작동합니다 . 그러나 다음과 같은 뷰 모델을 사용할 때 :

    public class TrainingDateEditViewModel
    {
        #region Properties

        /// <summary>
        /// Gets or sets CalendarEntry.
        /// </summary>
        public CalendarEntry CalendarEntry { get; set; }
....

생성 된 html 출력이 다음과 같기 때문에 클라이언트 유효성 검사가 더 이상 작동하지 않습니다.

<input type="text" value="" name="CalendarEntry.EndDate" id="CalendarEntry_EndDate" data-val-isdateafter-propertytested="StartDate" data-val-isdateafter-allowequaldates="True" data-val-isdateafter="End date needs to be after start date" data-val="true">

그리고

data-val-isdateafter-propertytested="StartDate" and IT SHOULD BE: "CalendarEntry.StartDate".

"CalendarEntry.StartDate"에 바인딩하는 것을 알 수 있도록 어떻게 만들 수 있습니까? rule.ValidationParameters [ "propertytested"] = this.testedPropertyName; // 여기에 전체 이름이 있어야합니다. ??? 어떻게??

감사


클라이언트 측 스크립트를 수정하여 테스트 된 요소의 접두사를 확인하고 다음과 같이 접두사 (있는 경우)를 선택기에 추가해야합니다.

$.validator.addMethod("isdateafter", function(value, element, params) {
    var parts = element.name.split(".");
    var prefix = "";
    if (parts.length > 1)
        prefix = parts[0] + ".";
    var startdatevalue = $('input[name="' + prefix + params.propertytested + '"]').val();
    if (!value || !startdatevalue) 
        return true;    
    return (params.allowequaldates) ? Date.parse(startdatevalue) <= Date.parse(value) :
        Date.parse(startdatevalue) < Date.parse(value);
});

Do not forget to include the client side inside this code. It tooks me hours to find that this was missing!

(function ($) {

  // your code here..

})(jQuery);

Just to fix a small error in counsellorben's javascript: the "(params.allowequaldates)" will be interpreted as a string (which will have a value of "False" or "True"), but that string will always be evaluated to true, thus always allowing equal dates. If you also want to allow for more levels of nesting of your objects than just 1, then you will get:

$.validator.addMethod("isdateafter", function(value, element, params) {
    var parts = element.name.split(".");
    var prefix = "";
    for (var i = 0; i < parts.length - 1; i++)
       prefix = parts[i] + ".";
    var startdatevalue = $('input[name="' + prefix + params.propertytested + '"]').val();
    if (!value || !startdatevalue) 
        return true;    
    var allowequal = params.allowequaldates.toLowerCase === "true";
    return allowequal ? Date.parse(startdatevalue) <= Date.parse(value) :
        Date.parse(startdatevalue) < Date.parse(value);
});

In the last answer there were some missing parenthesis on the call to toLowerCase, here is an updated version with document ready and the $.validator.unobtrusive...-part:

$(function () {
    $.validator.addMethod("isdateafter", function(value, element, params) {
        var parts = element.name.split(".");
        var prefix = "";
        for (var i = 0; i < parts.length - 1; i++) {
            prefix = parts[i] + ".";
        }

        var startdatevalue = $('input[name="' + prefix + params.propertytested + '"]').val();

        if (!value || !startdatevalue) return true;    

        var allowequal = params.allowequaldates.toLowerCase() === "true";
        return allowequal ? Date.parse(startdatevalue) <= Date.parse(value) :
            Date.parse(startdatevalue) < Date.parse(value);
    });
    $.validator.unobtrusive.adapters.add('isdateafter', 
        ['propertytested', 'allowequaldates'], 
        function (options) {
            options.rules['isdateafter'] = options.params;
            options.messages['isdateafter'] = options.message;
        });
});

참고URL : https://stackoverflow.com/questions/7025198/mvc-custom-validation-compare-two-dates

반응형