20

I am having trouble specifying the error message for the validation of a DateTime input value using data annotations in my model. I would really like to use the proper DateTime validator (as opposed to Regex, etc).

[DataType(DataType.DateTime, ErrorMessage = "A valid Date or Date and Time must be entered eg. January 1, 2014 12:00AM")]
public DateTime Date { get; set; }

I still get the default date validation message of "The field Date must be a date."

Am I missing something?

Chris Albrecht
  • 313
  • 1
  • 3
  • 8

5 Answers5

39

Add the following keys into Application_Start() at Global.asax

ClientDataTypeModelValidatorProvider.ResourceClassKey = "YourResourceName";
DefaultModelBinder.ResourceClassKey = "YourResourceName";

Create YourResourceName.resx inside App_GlobalResources folder and add the following keys

  • FieldMustBeDate The field {0} must be a date.
  • FieldMustBeNumeric The field {0} must be a number.
  • PropertyValueInvalid The value '{0}' is not valid for {1}.
  • PropertyValueRequired A value is required.
cilerler
  • 9,010
  • 10
  • 56
  • 91
  • This is the cleanest answer and does the job for me in MVC4. I've now got [Display], [Remote], data type and self-validating error messages being translated by the one set of resource files. Anyone know of an easy way to easily translate the form headings? – Gopher Nov 30 '13 at 00:01
  • For some reason I cannot get the correct translation for PropertyValueInvalid . It always shows the default one, while the other labels show the translated resource... – BTC Nov 27 '14 at 14:48
  • @user1073075 please create a new question with your code. if you may provide your question link here, I may look into that – cilerler Nov 27 '14 at 14:51
  • http://stackoverflow.com/questions/26890014/customised-error-messages-are-not-translated-in-asp-net-mvc-4 – BTC Nov 27 '14 at 14:55
  • 2
    Is there a way to achieve this when the resources are in a separate assembly? – RobHurd Mar 30 '15 at 02:56
  • 3
    Sadly this only works on a global scope. Is there any way to do the same thing for an individual field? – Martin Brown May 28 '15 at 16:41
16

I found one simple workaround.

You can leave your model untouched.

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

Then override the 'data-val-date' attribute in a view.

@Html.TextBoxFor(model => model.Date, new
{
    @class = "form-control",
    data_val_date = "Custom error message."
})

Or if you want to parameterize your messages, you can just use static function String.Format:

@Html.TextBoxFor(model => model.Date, new
{
    @class = "form-control",
    data_val_date = String.Format("The field '{0}' must be a valid date.",  
                                    Html.DisplayNameFor(model => model.Date))
})

Similar with resources:

@Html.TextBoxFor(model => model.Date, new
{
    @class = "form-control",
    data_val_date = String.Format(Resources.ErrorMessages.Date,  
                                   Html.DisplayNameFor(model => model.Date))
})
matthias_h
  • 11,356
  • 9
  • 22
  • 40
Pavol Korvas
  • 161
  • 1
  • 2
  • 1
    Interesting workaround. It's a bit hacky though since your embedding this code in specific views. It might work in a pinch, but I'd generally avoid this. – Chris Albrecht May 31 '15 at 03:50
  • 3
    I would avoid all the solutions in here, but MVC team created this unfortunate situation and for me it was the fastest/easiest quick fix even though I don't like it. Global scope does not work for my project, sadly. They should check if you've already put a "DataType" data annotation and remove their default validation in this case, and not give you that weird error : "Validation type names in unobtrusive client validation rules must be unique.". That way, you can put your own messages. – user627283 Jun 02 '16 at 18:50
  • Thank you! I feel like I'd tried just about everything under the sun to work around the lack of support for ErrorMessage in DataType. I agree with the other comments that it's slightly hacky in that it is specific to views, but I'm needing this for lack of HTML5 input date client support (looking at you Firefox). Thank you, this has saved me from spending even more time, and possibly saved my sanity too. – Reisclef Mar 03 '17 at 13:25
9

I have one dirty solution.

Create custom model binder:

public class CustomModelBinder<T> : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
        if(value != null && !String.IsNullOrEmpty(value.AttemptedValue))
        {
            T temp = default(T);
            try
            {
                temp = ( T )TypeDescriptor.GetConverter(typeof(T)).ConvertFromString(value.AttemptedValue);
            }
            catch
            {
                bindingContext.ModelState.AddModelError(bindingContext.ModelName, "A valid Date or Date and Time must be entered eg. January 1, 2014 12:00AM");
                bindingContext.ModelState.SetModelValue(bindingContext.ModelName, value);
            }

            return temp;
        }
        return base.BindModel(controllerContext, bindingContext);
    }
}

And then in Global.asax.cs:

protected void Application_Start()
{
    //...
    ModelBinders.Binders.Add(typeof(DateTime), new CustomModelBinder<DateTime>());
x2.
  • 9,554
  • 6
  • 41
  • 62
  • 1
    I don't think x2 suggested a dirty solution. Rather I believe mvc team did a dirty solution by hard-coding the error message. Giving the message in resource file as per @cilerler is okay, still the developer lacks the freedom to write model specific custom message. It really sucks! So I go with x2 to override the DefaultModelBinder with a CustomModelBinder, with some changes to pick the ValueError message from bindingContext.ModelMetadata.AdditionalValues and now I can write model specific custom messages by adding an attribute AdditionalMetadata("ValueError","write d message you wish")] – vinodpthmn Sep 11 '13 at 10:00
  • I agree with you eka. In hindsight, I see this is a very acceptable answer. – Chris Albrecht Sep 15 '13 at 02:20
  • Most acceptable answer i found for this crappy situation so far. – SomeRandomName Feb 16 '16 at 15:18
1

I got around this issue by modifying the error in the ModelState collection at the start of my action method. Something like this:

    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult MyAction(MyModel model)
    {
        ModelState myFieldState = ModelState["MyField"];
        DateTime value;
        if (!DateTime.TryParse(myFieldState.Value.AttemptedValue, out value))
        {
            myFieldState.Errors.Clear();
            myFieldState.Errors.Add("My custom error message");
        }

        if (ModelState.IsValid)
        {
            // Do stuff
        }
        else
        {
            return View(model);
        }
    }
Martin Brown
  • 24,692
  • 14
  • 77
  • 122
  • Putting this logic inside controller actions doesn't sound like a very good solution to me. – Chris Albrecht May 31 '15 at 03:47
  • 1
    I've yet to see a "good" solution to this, so I guess we are left to pick the best of a bad bunch. This does have the benefit over the top voted answer of being able to apply to only one field in your site rather than globally changing the validation for all date fields. – Martin Brown May 31 '15 at 18:56
  • I don't like this solution, but it is the best I could find ;) – AGuyCalledGerald Mar 02 '16 at 09:18
0

Try with a regular expression annotation like

[Required]
[RegularExpression("\d{4}-\d{2}-\d{2}(?:\s\d{1,2}:\d{2}:\d{2})?")]
public string Date { get; set; }

or check this

Community
  • 1
  • 1
Sandy
  • 2,429
  • 7
  • 33
  • 63
  • I'd like to avoid a regular expression annotation so I can still have all the flexibility of the default DateTime input validation/parsing.. i.e 2014-01-01, 2014-01-01 12:00, Jan 1 2014, January 1, 2014 12:00 AM, or any other valid permutation – Chris Albrecht Mar 13 '13 at 04:57
  • Did you check that link then? – Sandy Mar 13 '13 at 04:59
  • I was able to get custom validation attribute working for validation, but still no luck overriding the default error message. I even tried manually setting this.ErrorMessage from within the IsValid() function, but it says "The value 'xx' is not valid for Date". – Chris Albrecht Mar 13 '13 at 05:25
  • Validation is happening on the model data, unfortunately this message is thrown right from the DefaultModelBinder (well before the validation) either you have to go with @x2 or cilerler – vinodpthmn Sep 11 '13 at 09:48