web-dev-qa-db-ja.com

MVCカスタム検証:2つの日付を比較する

2つの日付を比較し、2番目の日付が最初の日付よりも大きいことを確認するカスタム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;にバインドすることがわかるように、どのように作成しますか。 //完全な名前である必要がありますか?どうやって??

ありがとう

62
ShaneKm

次のように、テストする要素のプレフィックスを確認するためにクライアント側のスクリプトを変更し、セレクタにプレフィックスがある場合はそれを追加する必要があります。

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

このコード内にクライアント側を含めることを忘れないでください。これが見つからないことを見つけるのに何時間もかかりました!

(function ($) {

  // your code here..

})(jQuery);
4
MrFlo

Counsellorbenのjavascriptの小さなエラーを修正するために、「(params.allowequaldates)」は文字列(「False」または「True」の値を持つ)として解釈されますが、その文字列は常にtrueに評価されます。したがって、常に同じ日付を許可します。また、オブジェクトのネストを1つよりも多くのレベルに許可する場合は、次のようになります。

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

最後の回答では、toLowerCaseの呼び出しで括弧が欠落していました。これは、ドキュメントの準備が整った更新バージョンと$ .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;
        });
});
0
Dan Pettersson