Optgroup drop-down support in MVC - Problems with Model Binding
Asked Answered
T

3

11

I wonder if anyone can shed some light on this problem..

I've got an option group drop-down for selecting a person's ethnicity – however it’s not storing the value in the model.

ViewModel

    [UIHint("EthnicOriginEditorTemplate")]
    [DisplayName("Question 6: Ethnic Origin")]
    public int EthnicOrigin { get; set; }

Helper : GroupDropList.Cs

using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using System.Web.Routing;

namespace Public.Helpers
{
    public static class GroupDropListExtensions
    {
        public static string GroupDropList(this HtmlHelper helper, string name, IEnumerable<GroupDropListItem> data, int SelectedValue, object htmlAttributes)
        {
            if (data == null && helper.ViewData != null)
                data = helper.ViewData.Eval(name) as IEnumerable<GroupDropListItem>;
            if (data == null) return string.Empty;

            var select = new TagBuilder("select");

            if (htmlAttributes != null)
                select.MergeAttributes(new RouteValueDictionary(htmlAttributes));

            select.GenerateId(name);

            var optgroupHtml = new StringBuilder();
            var groups = data.ToList();
            foreach (var group in data)
            {
                var groupTag = new TagBuilder("optgroup");
                groupTag.Attributes.Add("label", helper.Encode(group.Name));
                var optHtml = new StringBuilder();
                foreach (var item in group.Items)
                {
                    var option = new TagBuilder("option");
                    option.Attributes.Add("value", helper.Encode(item.Value));
                    if (SelectedValue != 0 && item.Value == SelectedValue)
                        option.Attributes.Add("selected", "selected");
                    option.InnerHtml = helper.Encode(item.Text);
                    optHtml.AppendLine(option.ToString(TagRenderMode.Normal));
                }
                groupTag.InnerHtml = optHtml.ToString();
                optgroupHtml.AppendLine(groupTag.ToString(TagRenderMode.Normal));
            }
            select.InnerHtml = optgroupHtml.ToString();
            return select.ToString(TagRenderMode.Normal);
        }
    }

    public class GroupDropListItem
    {
        public string Name { get; set; }
        public List<OptionItem> Items { get; set; }
    }

    public class OptionItem
    {
        public string Text { get; set; }
        public int Value { get; set; }
    }
}

This is my EditorTemplate

<%@ Import Namespace="Public.Helpers"%>
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<int>"%>

<%=Html.GroupDropList("EthnicOrigin",
                                 new[]
                                    {                                        
                                        new GroupDropListItem
                                             {
                                                 Name = "Ethnicity",
                                                 Items = new List<OptionItem>
                                                         {
                                                             new OptionItem {Value = 0, Text = "Please Select"}
                                                         }
                                             },

                                        new GroupDropListItem
                                             {
                                                 Name = "a) White",
                                                 Items = new List<OptionItem>
                                                         {
                                                             new OptionItem {Value = 1, Text = "British"},
                                                             new OptionItem {Value = 2, Text = "Irish"},
                                                             new OptionItem {Value = 3, Text = "Other White (Please specify below)"}
                                                         }
                                             },

                                         --snip

                                     }, Model, null)%>

And in the view I'm referencing it as:

<%=Html.EditorFor(x => x.EthnicOrigin, "EthnicOriginEditorTemplate")%>

However it's not passing through the selected Value into the model... has anyone experienced similar problems... many thanks in advance for some pointers.

Tyrr answered 10/11, 2010 at 9:33 Comment(2)
Just a note - I've also tried this with string instead of int as the option value and it still doesn't bind.Tyrr
Please Check Now Support For Optgroup In Dropdownlist .Net MVC‎Ginder
F
8

Your select doesn't have a name attribute and so when you submit the form the selected value is not sent to the server. You need to add a name:

select.GenerateId(name);
select.MergeAttribute("name", name);
Foxing answered 10/11, 2010 at 12:47 Comment(0)
C
5

Just changed the helper class to get it work for MVC 3 and with nullable int. Thanks a lot for the class, saves me plenty of time.

public static class GroupDropListExtensions
{
    public static MvcHtmlString GroupDropList(this HtmlHelper helper, string name, IEnumerable<GroupDropListItem> data, int? SelectedValue, object htmlAttributes)
    {
        if (data == null && helper.ViewData != null)
            data = helper.ViewData.Eval(name) as IEnumerable<GroupDropListItem>;
        if (data == null) return new MvcHtmlString(string.Empty);

        var select = new TagBuilder("select");

        if (htmlAttributes != null)
            select.MergeAttributes(new RouteValueDictionary(htmlAttributes));

        select.GenerateId(name);
        select.MergeAttribute("name", name);

        var optgroupHtml = new StringBuilder();
        var groups = data.ToList();
        foreach (var group in data)
        {
            var groupTag = new TagBuilder("optgroup");
            groupTag.Attributes.Add("label", helper.Encode(group.Name));
            var optHtml = new StringBuilder();
            foreach (var item in group.Items)
            {
                var option = new TagBuilder("option");
                option.Attributes.Add("value", helper.Encode(item.Value));
                if (SelectedValue != 0 && item.Value == SelectedValue)
                    option.Attributes.Add("selected", "selected");
                option.InnerHtml = helper.Encode(item.Text);
                optHtml.AppendLine(option.ToString(TagRenderMode.Normal));
            }
            groupTag.InnerHtml = optHtml.ToString();
            optgroupHtml.AppendLine(groupTag.ToString(TagRenderMode.Normal));
        }
        select.InnerHtml = optgroupHtml.ToString();
        return new MvcHtmlString(select.ToString(TagRenderMode.Normal));
    }
}

public class GroupDropListItem
{
    public string Name { get; set; }
    public List<OptionItem> Items { get; set; }
}

public class OptionItem
{
    public string Text { get; set; }
    public int Value { get; set; }
}
Cupcake answered 27/7, 2011 at 7:30 Comment(0)
C
1

This is supported natively using SelectListGroup as of ASP.NET MVC 5.2:

var items = new List<SelectListItem>(); 
var group1 = new SelectListGroup() { Name = "Group 1" };
items.Add(new SelectListItem() { Text = "Item1", Group = group1 }); 

Then in MVC, do

@Html.DropDownList("select", items) 
Cartomancy answered 16/6, 2016 at 15:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.