MVC model validation for date
Asked Answered
S

3

23

Is there any default validation for MVC 5 where I can set min and max value of date?

In my model i want date validation

    public class MyClass
    {               
        [Required(ErrorMessage="Start date and time cannot be empty")]
        //validate:Must be greater than current date
        [DataType(DataType.DateTime)]
        public DateTime StartDateTime { get; set; }

        [Required(ErrorMessage="End date and time cannot be empty")]
        //validate:must be greater than StartDate
        [DataType(DataType.DateTime)]
        public DateTime EndDateTime { get; set; }            
    }

Ps: According to this Asp.Net Website, there is a problem in using the Range validator for DateTime, and it is not recommended.

Note: jQuery validation does not work with the Range attribute and DateTime. For example, the following code will always display a client side validation error, even when the date is in the specified range:

[Range(typeof(DateTime), "1/1/1966", "1/1/2020")]

You will need to disable jQuery date validation to use the Range attribute with DateTime. It's generally not a good practice to compile hard dates in your models, so using the Range attribute and DateTime is discouraged.

I also, know that there are Nuget packages like Fluent Validation and Foolproof that does the trick for validating and checking if one date is greater than other, but I wanted to know if by default there is something to check the date's min and max value.

From what I saw in Whats new in MVC 5.1, There is support for MaxLength and MinLength validation.

Step answered 14/2, 2014 at 10:54 Comment(2)
You could make a custom validatorTreasury
#40543973 see this answerAbri
O
20

There is no need to disable jQuery date validation (and that is likely to cause other issues). You just need to override the range method of the $.validator.

By default, it works with numeric values (and then falls back to a string comparison), so you can add the following script (after jquery.validate.js and jquery.validate.unobtrusive.js, but not wrapped in $(document).ready

$.validator.methods.range = function(value, element, param) {
    if ($(element).attr('data-val-date')) {
        var min = $(element).attr('data-val-range-min');
        var max = $(element).attr('data-val-range-max');
        var date = new Date(value).getTime();
        var minDate = new Date(min).getTime();
        var maxDate = new Date(max).getTime();
        return this.optional(element) || (date >= minDate && date <= maxDate);
    }
    // use the default method
    return this.optional( element ) || ( value >= param[ 0 ] && value <= param[ 1 ] );
};

Then you can use the RangeAttribute on your property

[Range(typeof(DateTime), "1/1/1966", "1/1/2020")]
public DateTime Date { get; set; }
Of answered 4/2, 2017 at 4:55 Comment(3)
Can anyone give an explanation on how to implement this solution? How do I add the script provided?Intercollegiate
@Klicker just put this JS section inside of $(function () { $.validator.methods.range = function... } and use the Range attribute in your Input ModelApothegm
In ASP.NET Core 5.0, 'data-val-date' is not added to the input element so you have to manually add it to the input tag helper. Also, If you set the binding to [DataType(DataType.Date)], then it will not include the time part in the input element value. Date(value) needs to have a time value or the minDate check fails by one day off.Rationality
G
12

I'd do this with the IValidatableObject interface from System.ComponentModel.DataAnnotations, which allows you to add extra validation rules where you can do a lot more checking. Add the interface to your class, and then implement the Validate method, where you can compare the StartDateTime against the current date/time, and also compare the EndDateTime with the StartDateTime, e.g.

public class MyClass : IValidatableObject
{               
    [Required(ErrorMessage="Start date and time cannot be empty")]
    //validate:Must be greater than current date
    [DataType(DataType.DateTime)]
    public DateTime StartDateTime { get; set; }

    [Required(ErrorMessage="End date and time cannot be empty")]
    //validate:must be greater than StartDate
    [DataType(DataType.DateTime)]
    public DateTime EndDateTime { get; set; }       

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        List<ValidationResult> results = new List<ValidationResult>();

        if (StartDateTime < DateTime.Now)
        {
            results.Add(new ValidationResult("Start date and time must be greater than current time", new []{"StartDateTime"}));
        }

        if (EndDateTime <= StartDateTime)
        {
            results.Add(new ValidationResult("EndDateTime must be greater that StartDateTime", new [] {"EndDateTime"}));
        }

        return results;
    }     
}

The only potential drawback to this is that Validate runs server-side, not in jQuery validation.

Gamete answered 6/12, 2016 at 12:25 Comment(1)
How would you get any of those error messages back to the web page?Rotman
H
1

Normally I use this solution when I want to validate something in the server. I know that you can rely on the Validation Model that MVC uses, but when I do the validations, I am trying to use a separate project in case I need to make unit test for them. Lets say you have two applications, one Desktop and one Web, then both can share the ValidationProject instead of repeating the code in both application for the same "View"/"Screen". The idea here is isolate the Validation Project as an independent project of your solution.

So you can download FluentValidation project from NuGet, FluentValidation uses Rules inside the constructor, you can see the documentation here https://github.com/JeremySkinner/FluentValidation/wiki

Your Date rule can be used ont this way for example, there you can put rules for your min value and your max value also:

public class CustomerValidator: AbstractValidator<Customer> {
  public CustomerValidator() {
                    RuleFor(customer => customer.startDate)
                    .NotNull()
                    .WithMessage("You should select a start date")
                    .Must(d => d.HasValue && d.Value <= DateTime.Today)
                    .WithMessage("The start date should not be greather than current month/year");
  }

Now inside your controller

[HttpPost]
public ActionResult Index(Customer c)
{
  if(ModelState.IsValid)
  {
    var validator= new CustomerValidator();
    var valResult = validator.Validate(c);

    if (valResult.Errors.Count != 0)
    {
       valResult.Errors.ForEach(x => ModelState.AddModelError(x.PropertyName, x.ErrorMessage));
       return View(vm);
    }

    //Your code here
  }
}
Hooke answered 6/12, 2016 at 14:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.