Validating time-only input in asp.net MVC unobtrusive validation
Asked Answered
T

3

16

I have two separate fields on the page: one for date and one for time.

This is the model:

[Required]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:hh:mm tt}")]
public DateTime? StartTime { get; set; }

[Required]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:MM/dd/yyyy}")]
public DateTime Date { get; set; }

This is the view:

@Html.TextBoxFor(m => m.Date, "{0:MM/dd/yyyy}", new { type = "text" })
@Html.TextBoxFor(m => m.StartTime, "{0:hh:mm tt}", new { type = "text", id = "timeStart" })

The javascript unobtrusive validation works fine with the Date field however when i enter "11:00 PM" or "11:00 pm" in StartTime the validation shows

"The field StartTime must be a date"

Server side validation works fine with "0:hh:mm tt" it's only the javascript that has a problem. For now i just disabled javascript validation but would like eventually to have it on this page

Can this be done for "time" field?

Thigmotropism answered 1/6, 2013 at 12:2 Comment(0)
C
16

Honestly the easiest way to achieve this is to use a regular expression validator for it. Here is an example.

[RegularExpression(@"^(0[1-9]|1[0-2]):[0-5][0-9] (am|pm|AM|PM)$", ErrorMessage = "Invalid Time.")]

The unobtrusive validation should work just fine with this expression.

Hope this can help you!

EDIT

I've fixed the regular expression which started throwing errors in the console because of some illegal characters. Also, you will need a string property wrapper for this property or else it will always look for a valid DateTime.

Below is what you should be binding to.

Model:

public DateTime? StartTime { get; set; }

[Required]
[RegularExpression(@"^(0[1-9]|1[0-2]):[0-5][0-9] (am|pm|AM|PM)$", ErrorMessage = "Invalid Time.")]
public string StartTimeValue
{
    get
    {
        return StartTime.HasValue ? StartTime.Value.ToString("hh:mm tt") : string.Empty;
    }

    set
    {
        StartTime = DateTime.Parse(value);
    }
}

View:

@Html.TextBoxFor(m => m.StartTimeValue)
Coelacanth answered 5/6, 2013 at 19:33 Comment(10)
doesn't fire on the client side. What do i need to do make RegEx validate in javascript first?Thigmotropism
After reviewing your code once again I realized that you are changing the ID to "timeStart". If you remove this, the validation should fire correctly.Coelacanth
I must be doing something wrong. the moment i add RegularExpression attribute it switches to server side validation onlyThigmotropism
Did you remove the id = "timeStart" part of the razor helper for the field?Coelacanth
yes, i removed id = "timeStart". in fact removed all additional attributesThigmotropism
See if my edited answer fixes the issue. If the client validation still doesn't fire after implementing this. It is most likely a different problem. You can check the console output for errors in your browser.Coelacanth
Well, yeah, this will work as it's a string now. Kind of defeats the purpose of mapping a model to a field. I'll use this if i can't find a nicer way. ThanksThigmotropism
let us continue this discussion in chatThigmotropism
@technicallyjosh: Great answer! Some readers may find changing the regular expression to ^(0?[1-9]|1[0-2]):[0-5][0-9] [aApP][mM]$ helpful. The 0? change near the beginning makes the leading zero optional for times before 10:00. The [aApP][mM] change near the end shows mercy to clumsy typists like myself who type aM, Am, pM, or Pm.Cruces
That validated it correctly. Does anybody know of way to get it working with the TimeSpan objects?Sector
R
11

Add DataType.Time attribute to your time field and use EditorFors to remove format duplication:

Model

    [Required]
    [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:hh:mm tt}")]
    [DataType(DataType.Time)]
    public DateTime? StartTime { get; set; }

    [Required]
    [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:MM/dd/yyyy}")]
    public DateTime Date { get; set; }

View

    @Html.EditorFor(m => m.Date, new { type = "text" })
    @Html.EditorFor(m => m.StartTime, new { type = "text", id = "timeStart" })
Rival answered 5/6, 2013 at 3:21 Comment(1)
@Html.EditorFor renders a text box with time controls (hours, minutes, seconds) in Chrome. it's not what i want, i want a text box because i use a third-party javascript control on top of my textbox for cross-browser compatibility. If i use TextBoxFor then the "DataType" attribute doesn't help. In fact i can enter "aaa" and it still passes validationThigmotropism
J
0

In your view model try this

    [Display(Name = "Start Time")]
    [Time]
    [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:hh:mm tt}")]
    public DateTime Time { get; set; }

and have the attribute class

public class TimeAttribute : ValidationAttribute, IClientValidatable
{
    public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata,
                                                                        ControllerContext context)
    {
        yield return new ModelClientValidationRule
        {
            ErrorMessage = ErrorMessage,
            ValidationType = "time"
        };
    }

    public override bool IsValid(object value)
    {
        DateTime time;
        if (value == null || !DateTime.TryParse(value.ToString(), out time))
            return false;

        return true;
    }
}

EDIT: I'm also aware that in some cases you need to add some scripting to the client html such as that found in this answer MVC3 unobtrusive validation group of inputs although I'm not exactly sure when its necessary. This answer should get you half way there. Unfortunately, I'm not sure this answer prevents the postback, but it does flag the model as invalid.

Joint answered 4/6, 2013 at 3:5 Comment(1)
Just tried this. Made no difference. It fact it never hit IsValid() method.Thigmotropism

© 2022 - 2024 — McMap. All rights reserved.