DropDownListFor Unobtrusive Validation Required Not getting correct attributes
Asked Answered
Y

3

8

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

Yorkist answered 19/10, 2011 at 22:24 Comment(0)
P
5

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.

Philippines answered 20/10, 2011 at 6:18 Comment(1)
I made the int nullable and it still fails to write the data validation elements in the controlYorkist
H
4

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.

Hadwin answered 13/1, 2012 at 15:48 Comment(2)
That is disappointing in the extreme. I extract all my select list from the model into ViewData so the list is accessible by a list name in a UIHInt based attribute.Girlie
Disappointing information, but useful to know. Sadly the real world rarely gets to always be strongly typed.Velazquez
C
0

Those who are here looking for the same behavior in Kendo dropdownlist, please add 'required' in your code.

@(Html.Kendo().DropDownListFor(m => m)
    .Name("FeatureId").BindTo((System.Collections.IEnumerable)ViewData[CompanyViewDataKey.Features])
    .DataValueField("Id")
    .DataTextField("Name")
    .OptionLabel("--Select--")
    .HtmlAttributes(new { title="Select Feature", required="required"})

)

[Required] attribute in the viewmodel did not work for me but adding the above in Htmlattributes did. Hope this helps someone.

Cum answered 12/10, 2017 at 10:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.