Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MVC2 Client-side validation for a DateTime?

What approach do you recommend for validating a DateTime on the client side in MVC?

Let's say I have a model with a property named DateOfBirth that is a DateTime, like so.

public class UserModel
{
    [DataType(DataType.Date)]
    public DateTime DateOfBirth {get;set;}
}

On the View, I have a simple

<%: Html.LabelFor(model=>model.DateOfBirth) %>
<%: Html.EditorFor(model=>model.DateOfBirth) %>
<%: Html.ValidationMessageFor(model=>model.DateOfBirth) %>
<input type="submit" value="Submit" />

I can use either the Microsoft MVC validations or the jQuery validations. How do I get the DateTime to validate client-side?

I realize all the DataTypeAttribute does is provide formatting hints and doesn't really do any validation (it leaves that part to the ModelBinder).

Basically I want to duplicate what the ModelBinder does when it tries to put the posted value into the model's DateOfBirth property.

What are your recommendations?

like image 671
Josh Avatar asked Aug 14 '10 21:08

Josh


2 Answers

As suggested above, follow Phil Haack's post on custom validations: http://haacked.com/archive/2009/11/19/aspnetmvc2-custom-validation.aspx

Here's how I would do it:


  1. Add a "DateFormatAttribute" class, like so:

    public class DateFormatAttribute : ValidationAttribute {
      public override bool IsValid(object value) {    
        if (value == null) {
          return true;
        }

        // Note: the actual server side validation still has to be implemented :-)
        // Just returning true now...

        return true;
      }
    }

  1. Add a "DateFormatValidator" class, like so:

    public class DateFormatValidator : DataAnnotationsModelValidator 
    {
      string message;

      public PriceValidator(ModelMetadata metadata, ControllerContext context
        , DateFormatAttribute attribute)
        : base(metadata, context, attribute) 
      {
        message = attribute.ErrorMessage;
      }

      public override IEnumerable GetClientValidationRules() 
      {
        var rule = new ModelClientValidationRule {
          ErrorMessage = message,
          ValidationType = "date" // note that this string will link to the JavaScript function we'll write later on
        };

        return new[] { rule };
      }
    }

  1. Register the above classes somewhere in Global.asax.cs:

    DataAnnotationsModelValidatorProvider
        .RegisterAdapter(typeof(DateFormatAttribute), typeof(DateFormatValidator));

  1. Add a validation function on the client. Note that this will have to be implemented attributng the locale of the user. The following is a Dutch (nl-NL, nl-BE) client-side validation function:

    /*
     * Localized default methods for the jQuery validation plugin.
     * Locale: NL
     */
    jQuery.extend(jQuery.validator.methods, {
        date: function(value, element) {
            return this.optional(element) || /^\d\d?[\.\/-]\d\d?[\.\/-]\d\d\d?\d?$/.test(value);
        }
    });

That should cover things...

like image 173
maartenba Avatar answered Oct 13 '22 11:10

maartenba


Josh,

your problem is quite a common problem in MVC, which is that the modelbinder is trying to BIND the inputted values from the form into the model. obviously, if it doesnt 'fit' you'll get an error straight away.

so how do I make the modelbinder use my custom validation? and return my error message?

well, first read and do the things written by phil haack. then you have your custom validation.

the next thing is. dont use Integers and datetimes in your model! If the user can input whatever he wants in a textbox, this always will give problems.

what you should do is, make a flatObject of your object.

a flatObject is pretty simple. It's an object, an exact copy of the variables inside, only, the inst and datetimes are strings (cos those always bind in the modelbinder)

example:

namespace MVC2_NASTEST.Models {

    public partial class FlatNieuw {
        public int Niw_ID { get; set; }
        public string Niw_Datum { get; set; }
        public string Niw_Titel { get; set; }
        public string Niw_Bericht { get; set; }
        public int Niw_Schooljaar { get; set; }
        public bool Niw_Publiceren { get; set; }
    }
}

the only ints i have are from the dropdowns, cos those dont fail, if the value in the dropdowns are ints. the date (datum) is a string. i do the custom validation on this string. the modelbinder binds to this FlatNieuw object.

my Nieuw class has exactly the same names of fields as this class. so when you are using UpdateModel() this still works. if you are making a new entry, you can use automapper to map this flatObject to your normal Object.

i think this, together with phil haack's blog should give you a hand on how to do this. if you have questions dont hesitate to ask.

like image 32
Stefanvds Avatar answered Oct 13 '22 10:10

Stefanvds