5

I'm trying to use the built in ASP.NET MVC 2 client side validation on a Select List like the following:

 private SelectList _CategoryList;
        [Required(ErrorMessage = "Category Required")]
        [System.ComponentModel.DataAnnotations.Range(1, double.MaxValue, ErrorMessage = "Please Select A Category")]
        [DisplayName("Category")]
        public SelectList CategoryList
        {
            get
            {
                return new SelectList(Categories, "CatID", "CatFullName"); ;
            }
            set
            {
                _CategoryList = value;
            }
        }

However it's not working...if the default value which is 0 is selected the validation message does not appear and the page progresses as though it's validated. Thoughts?

2

3 Answers 3

6

Ok so I found the answer in an answer to a slightly different question. So I'm posting my complete code here, which extends on Scott Guthries ASP.NET MVC 2 Validation post: http://weblogs.asp.net/scottgu/archive/2010/01/15/asp-net-mvc-2-model-validation.aspx

My ViewModel:

public class Person
{
    [Required(ErrorMessage="First Name Required")]
    [StringLength(50,ErrorMessage="Must be under 50 characters")]
    public string FirstName { get; set; }

    [Required(ErrorMessage="Last Name Required")]
    [StringLength(50, ErrorMessage = "Must be under 50 characters")]
    public string LastName { get; set; }

    [Required(ErrorMessage="Age Required")]
    [Range(1,120,ErrorMessage="Age Must be between 0 and 120")]
    public int Age { get; set; }

    [Required(ErrorMessage="Email Required")]
    public string Email { get; set; }


    public IEnumerable<SelectListItem> FavoriteColor { get; set; }


    [Range(0, 6, ErrorMessage = "Out of range")]
    public int SelectedFavColor { get; set; }
}

My Color class:

public class Colors
{
    public int ColorID { get; set; }
    public string ColorName { get; set; }
}

My list helper extensions stolen from Rob Connery, who stole it from someone else:

public static class ListExtensions
{
    public static IEnumerable<T> ForEach<T>(this IEnumerable<T> collection, Action<T> action)
    {
        foreach (var item in collection) action(item);
        return collection;
    }

    public static SelectList ToSelectList<T>(this IEnumerable<T> collection)
    {
        return new SelectList(collection, "Key", "Value");
    }

    public static SelectList ToSelectList<T>(this IEnumerable<T> collection, string selectedValue)
    {
        return new SelectList(collection, "Key", "Value", selectedValue);
    }

    public static SelectList ToSelectList<T>(this IEnumerable<T> collection,
                         string dataValueField, string dataTextField)
    {
        return new SelectList(collection, dataValueField, dataTextField);
    }

    public static SelectList ToSelectList<T>(this IEnumerable<T> collection,
                         string dataValueField, string dataTextField, string selectedValue)
    {
        return new SelectList(collection, dataValueField, dataTextField, selectedValue);
    }
}

My Controller Code (yes it could be refactored to be more DRY):

public ActionResult Create()
    {
        Person newFriend = new Person();
        IList<Colors> colorslist = new List<Colors>();
        colorslist.Add(new Colors { ColorID = -1, ColorName = "Please Select Color" });
        colorslist.Add(new Colors { ColorID = 1, ColorName = "Red" });
        colorslist.Add(new Colors { ColorID = 2, ColorName = "Green" });
        colorslist.Add(new Colors { ColorID = 3, ColorName = "Blue" });

        newFriend.FavoriteColor = colorslist.ToSelectList("ColorID","ColorName","-1");
        return View(newFriend);
    }

    [HttpPost]
    public ActionResult Create(Person friendToCreate, FormCollection collection)
    {
        friendToCreate.SelectedFavColor = Convert.ToInt32(collection["SelectedFavColor"]);
        if (ModelState.IsValid)
        {
            return Redirect("/");
        }
        IList<Colors> colorslist = new List<Colors>();
        colorslist.Add(new Colors { ColorID = -1, ColorName = "Please Select Color" });
        colorslist.Add(new Colors { ColorID = 1, ColorName = "Red" });
        colorslist.Add(new Colors { ColorID = 2, ColorName = "Green" });
        colorslist.Add(new Colors { ColorID = 3, ColorName = "Blue" });
        friendToCreate.FavoriteColor = colorslist.ToSelectList("ColorID", "ColorName");
        return View(friendToCreate);
    }

My page markup:

<% using (Html.BeginForm()) {%>

    <fieldset>
        <legend>Fields</legend>

        <div class="editor-label">
            <%= Html.LabelFor(model => model.FirstName) %>
        </div>
        <div class="editor-field">
            <%= Html.TextBoxFor(model => model.FirstName) %>
            <%= Html.ValidationMessageFor(model => model.FirstName) %>
        </div>

        <div class="editor-label">
            <%= Html.LabelFor(model => model.LastName) %>
        </div>
        <div class="editor-field">
            <%= Html.TextBoxFor(model => model.LastName) %>
            <%= Html.ValidationMessageFor(model => model.LastName) %>
        </div>

        <div class="editor-label">
            <%= Html.LabelFor(model => model.Age) %>
        </div>
        <div class="editor-field">
            <%= Html.TextBoxFor(model => model.Age) %>
            <%= Html.ValidationMessageFor(model => model.Age) %>
        </div>

        <div class="editor-label">
            <%= Html.LabelFor(model => model.Email) %>
        </div>
        <div class="editor-field">
            <%= Html.TextBoxFor(model => model.Email) %>
            <%= Html.ValidationMessageFor(model => model.Email) %>
        </div>

        <div class="editor-label">
            <%= Html.LabelFor(model => model.FavoriteColor) %>
        </div>
        <div class="editor-field">
            <%= Html.DropDownList("SelectedFavColor", Model.FavoriteColor, -1)%>
            <%= Html.ValidationMessageFor(model => model.SelectedFavColor) %>
        </div>

        <p>
            <input type="submit" value="Submit" />
        </p>
    </fieldset>

<% } %>
Sign up to request clarification or add additional context in comments.

Comments

1

When I work with my ViewModel, I'd have a property CategoryId and put my range validator on that, not the dropdown. The Selectlist just provides the data - you validate against the model.

 [Required(ErrorMessage = "Category Required")]
    [System.ComponentModel.DataAnnotations.Range(1, double.MaxValue, ErrorMessage = "Please Select A Category")]
    [DisplayName("Category")]
    public int CategoryId {get;set;}

On the view I'd have my dropdown with the id for my category but the list from my Categories:

<%= Html.DropDownList("CategoryId", (SelectList)Model.Categories, "(Select)")%>    

when your data posts back to the server, you should observe that the class contains the id value.

1 Comment

When I try your code it tells me that CategoryId is of type int, but it needs to be a IEnumerable<SelectListItem>. So I changed it...but then it thinks every value is a string. Hmmmm
0

I don't think it has to do with DataAnnotations because it happens without them as well when you have a model bound to an entity with a non-nullable and you try to put an invalid value in. What I have done is to send along ModelState["XXXX"].Value.AttemptedValue from the form and validate against that instead of the property in the entity. I wonder if validating against the raw form data entirely instead of just the problem items is more appropriate.

A similar reply: ASP.NET MVC: DataAnnotations - Show an error message indicating that a field must be numeric

I also posed a similar question: ASP.NET MVC. Validation fails on dropdown no matter the value

Comments

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.