Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

DropDownListFor Unobtrusive Validation Required Not getting correct attributes

This Question is similar but the accepted answer solves it server side, I'm interested in client side solutions.

Given this ViewModel

public class MyViewModel
{
    public string ID { get; set; }

    [Required(ErrorMessage = "I DEMAND YOU MAKE A CHOICE!")]
    [Display(Name = "Some Choice")]
    public int SomeChoice{ get; set; }   
    [Required(ErrorMessage = "I DEMAND YOU MAKE A CHOICE!")]
    [Display(Name = "Keyword")]
    public string Keyword { get; set; }    
}

and the razor

<div>
@Html.LabelFor(model => model.SomeChoice, new { @class = "label" })
@Html.DropDownListFor(model => model.SomeChoice, (SelectList)ViewBag.SomeChoice, "Select...")
@Html.ValidationMessageFor(model => model.SomeChoice)
</div>

and assume ViewBag.SomeChoice contains a select list of choices

The rendered html doesn't get the data-val="true" data-val-required="I DEMAND YOU MAKE A CHOICE!" attributes in it like @Html.EditorFor(model => model.Keyword) or @Html.TextBoxFor would render.

WHY?

Adding a class = "required" to it like so

@Html.DropDownListFor(model => model.SomeChoice, (SelectList)ViewBag.SomeChoice, "Select...", new { @class = "required" })

which uses the jQuery Validation class semantics and blocks on submit but doesn't display the message. I can do this kind of thing

@Html.DropDownListFor(model => model.SomeChoice, (SelectList)ViewBag.SomeChoice, "Select...", new Dictionary<string, object> { { "data-val", "true" }, { "data-val-required", "I DEMAND YOU MAKE A CHOICE!" } })

Which will put the right attributes there, and blocks on submit and shows the message but doesn't take advantage of the RequiredAttribute ErrorMessage I have on my ViewModel

So has anyone written a DropDownListFor that behaves like the other HtmlHelpers with regard to Validation?

EDIT Here is my EXACT code

In HomeController.cs

  public class MyViewModel
  {
    [Required(ErrorMessage = "I DEMAND YOU MAKE A CHOICE!")]
    [Display(Name = "Some Choice")]
    public int? SomeChoice { get; set; }
  }


    public ActionResult About()
    {
        var items = new[] { new SelectListItem { Text = "A", Value = "1" }, new SelectListItem { Text = "B", Value = "2" }, new SelectListItem { Text = "C", Value = "3" }, };
        ViewBag.SomeChoice = new SelectList(items,"Value", "Text");
        ViewData.Model = new MyViewModel {};
        return View();
    }

About.cshtml

@using Arc.Portal.Web.Host.Controllers
@model MyViewModel
<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())
{
<div>
    @Html.LabelFor(model => model.SomeChoice)
    @Html.DropDownListFor(model => model.SomeChoice, (SelectList)ViewBag.SomeChoice, "Select...")
    @Html.ValidationMessageFor(model => model.SomeChoice)
</div>

<button type="submit">OK</button>
}

And here is the rendered code

<form action="/Home/About" method="post">    <div>
    <label for="SomeChoice">Some Choice</label>
    <select id="SomeChoice" name="SomeChoice"><option value="">Select...</option>
<option value="1">A</option>
<option value="2">B</option>
<option value="3">C</option>
</select>
    <span class="field-validation-valid" data-valmsg-for="SomeChoice" data-valmsg-replace="true">    </span>
</div>
<button type="submit">OK</button>
</form>

It posts back to my controller...this shouldn't happen

like image 945
Peter Avatar asked Oct 19 '11 22:10

Peter


2 Answers

Simply use a nullable integer on the property you are binding the dropdownlist to on your view model:

[Required(ErrorMessage = "I DEMAND YOU MAKE A CHOICE!")]
[Display(Name = "Some Choice")]
public int? SomeChoice { get; set; }  

Also in order to get proper unobtrusive HTML5 data-* attributes the dropdown must be inside a form:

@model MyViewModel
<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())
{
    <div>
        @Html.LabelFor(model => model.SomeChoice, new { @class = "label" })
        @Html.DropDownListFor(
            model => model.SomeChoice, 
            Model.ListOfChoices, 
            "Select..."
        )
        @Html.ValidationMessageFor(model => model.SomeChoice)
    </div>

    <button type="submit">OK</button>
}

Also you will notice that I got rid of ViewBag (which I simply cannot stand) and replaced it with a corresponding property on your view model which will contain the possible choices for the dropdown.

like image 188
Darin Dimitrov Avatar answered Oct 11 '22 03:10

Darin Dimitrov


I had the same problem. And noticed that this happens when dropDownList is populated from ViewBag or ViewData. If you would write @Html.DropDownListFor(model => model.SomeChoice, Model.SomeChoice, "Select...") as in above example validation attributes would be wrote.

like image 37
Kazys Avatar answered Oct 11 '22 03:10

Kazys