2

I wrote my own HtmlHelper Extension Method to display a time-input.

public static MvcHtmlString TimePickerFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
        {
            if (expression == null)
            {
                throw new ArgumentNullException("expression");
            }

            string expressionText = ExpressionHelper.GetExpressionText(expression);
            string value = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData).Model.ToString();
            return TimePicker(htmlHelper, expressionText, value);
        }

        public static MvcHtmlString TimePicker(this HtmlHelper helper, string name, string value)
        {
            TagBuilder builder = new TagBuilder("input");
            builder.MergeAttribute("type", "time");
            builder.MergeAttribute("id", name);
            builder.MergeAttribute("name", name);

            if (value != null)
            {
                builder.MergeAttribute("value", value);
            }

            return MvcHtmlString.Create(builder.ToString(TagRenderMode.SelfClosing));
        }

This is how I use it:

@Html.TimePickerFor(model => model.StartTime)

When the time is not valid, I add an error like this:

ModelState.AddModelError("StartTime", "The selected time is already taken.");

When this error is added, it should add the input-validation-error class to the input, but it doesn't. I cannot get it to work. It does show the error in the ValidationSummary, and it adds the class to the default inputs. Any help would be appreciated!

I already tried adding it like this, but this still won't add the class:

var validation = helper.GetUnobtrusiveValidationAttributes(name);
foreach (KeyValuePair<string, Object> attribute in validation)
{
    builder.MergeAttribute(attribute.Key, attribute.Value.ToString());
}

Edit: temporary workaround found, haven't been able to solve the problem though

ModelState modelState;
if (helper.ViewData.ModelState.TryGetValue(name, out modelState))
{
    if (modelState.Errors.Count > 0)
         builder.AddCssClass("input-validation-error");
}

1 Answer 1

3

What you are doing is not a workaround! that is how you are supposed to do it

string fullName = helper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name);
ModelState modelState;
if (helper.ViewData.ModelState.TryGetValue(fullName, out modelState))
{
    if (modelState.Errors.Count > 0)
    {
        builder.AddCssClass(HtmlHelper.ValidationInputCssClassName);
    }
}

The purpose of GetUnobtrusiveValidationAttributes is to return the attributes for unobtrusive validation to work. These attributes start with the name "data-val" and is used by jquery.validate.unobtrusive.js

<input data-val="true" data-val-required="Please supply the title." id="Title" name="Title" type="time">

Your custom helper must have both in order to work properly.

For reference, read the source code of InputExtensions here

BUT wait!

HTML5 time input is actually natively supported by MVC! You don't need to write your own.

public class TestModel
{
    [DataType(DataType.Time)]
    public DateTime MyTime { get; set; }
}

@Html.EditorFor(model => model.MyTime)
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for your reply, that's quite a coincidence haha! I wrote it myself by putting together some scripts I found. I used the DataType.Time, however some things weren't working out for me for dates and times. I wasn't able to set a default date or min/max dates if I remember correctly. Thanks for your help and the useful source code.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.