Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.Net MVC 3 unobtrusive client validation does not work with drop down lists

I have a simple drop down list, the first item in the list has an empty value. If I do not select anything in the list the client validation ignores it. I have that field set up as required on the model using annotation attributes.

 @Html.DropDownListFor(model => Model.CCPayment.State, UnitedStatesStates.StateSelectList)



[Required(ErrorMessage = "State is Required.")]
    public string State
    {
        get
        {
            return _state;
        }
        set
        {
            _state = value;
        }
    }

any ideas? am I missing something?

like image 231
JBeckton Avatar asked Jan 25 '11 23:01

JBeckton


People also ask

How do I make sure client validation is enabled in MVC?

We can enable and disable the client-side validation by setting the values of ClientValidationEnabled & UnobtrusiveJavaScriptEnabled keys true or false. This setting will be applied to application level. For client-side validation, the values of above both the keys must be true.

How can use client side validation in ASP.NET MVC?

ASP.NET MVC supports client-side validation using jQyery. First, you need to take a reference of two javascript files from the Scripts folder, jquery. validate. unobtrusive.

What is unobtrusive validation in ASP.NET MVC?

An unobtrusive validation in jQuery is a set of ASP.Net MVC HTML helper extensions.By using jQuery Validation data attributes along with HTML 5 data attributes, you can perform validation to the client-side.

What does validator unobtrusive parse do?

validator. unobtrusive. parse(selector) method to force parsing. This method parses all the HTML elements in the specified selector and looks for input elements decorated with the [data-val=true] attribute value and enables validation according to the data-val-* attribute values.


2 Answers

It looks like a legitimate bug, here's the best workaround I've found in my search:

http://forums.asp.net/t/1649193.aspx

In short. You wrap the source of the problem, DropDownListFor, in a custom Html extension and you manually retrieve the unobtrusive clientside validation rules like this:

IDictionary<string, object> validationAttributes = htmlHelper.
    GetUnobtrusiveValidationAttributes(
        ExpressionHelper.GetExpressionText(expression),
        metadata
    );

Then you combine your validationAttributes dictionary with any other html attributes passed into your custom helper and you pass that along to DropDownListFor

The complete code that I'm using (I have a label in there too, you can feel free to de-couple):

public static IHtmlString DropDownListWithLabelFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression, string label, IEnumerable<SelectListItem> items, string blankOption, object htmlAttributes = null)
{
    var l = new TagBuilder("label");
    var br = new TagBuilder("br");

    var metadata = ModelMetadata.FromLambdaExpression(expression, helper.ViewData);
    var mergedAttributes = helper.GetUnobtrusiveValidationAttributes(ExpressionHelper.GetExpressionText(expression), metadata);

    if (htmlAttributes != null)
    {
        foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(htmlAttributes))
        {
            object value = descriptor.GetValue(htmlAttributes);
            mergedAttributes.Add(descriptor.Name, value);
        }
    }

    l.InnerHtml = label + br.ToString(TagRenderMode.SelfClosing) + helper.DropDownListFor(expression, items, blankOption, mergedAttributes);
    return MvcHtmlString.Create(l.ToString(TagRenderMode.Normal));
}
like image 167
Milimetric Avatar answered Nov 01 '22 17:11

Milimetric


You have provided too little information in order for us to be able to pinpoint the problem. You might have forgot to include the proper unobtrusive validation scripts inside your view but who knows? You haven't shown your view.

Here's a full working example:

Model:

public class MyViewModel
{
    [Required(ErrorMessage = "State is Required.")]
    public string State { get; set; }

    public IEnumerable<SelectListItem> States 
    { 
        get
        {
            return Enumerable.Range(1, 5).Select(x => new SelectListItem
            {
                Value = x.ToString(),
                Text = "state " + x
            });
        }
    }
}

Controller:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View(new MyViewModel());
    }

    [HttpPost]
    public ActionResult Index(MyViewModel model)
    {
        return View(model);
    }
}

View:

@model AppName.Models.MyViewModel
@{
    ViewBag.Title = "Home Page";
}
<script src="@Url.Content("~/Scripts/jquery.validate.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.js")" type="text/javascript"></script>

@using (Html.BeginForm())
{
    @Html.LabelFor(x => x.State)
    @Html.DropDownListFor(
        x => x.State, 
        new SelectList(Model.States, "Value", "Text"), 
        "-- Please select a state --"
    )
    @Html.ValidationMessageFor(x => x.State)
    <input type="submit" value="OK" />
}

Notice how we are providing a default value in the DropDownListFor helper as last parameter. That will insert an option in the beginning with empty value and custom text and if the user doesn't pick some state the required validator should kick in.

like image 4
Darin Dimitrov Avatar answered Nov 01 '22 17:11

Darin Dimitrov