SelectListItem with data-attributes

63,873

Solution 1

Here's the simple solution.

Not everything has to be written with extension method in .NET code. One of the great things about MVC is it gives you easy access to construct your own HTML.

With MVC4 you can get the id and name of the element on the expression tree with the helpers HTML.NameFor and HTML.IdFor

<select name="@Html.NameFor(Function(model) model.CityId)"
        id="@Html.IdFor(Function(model) model.CityId)"
        class="location_city_input">
    @For Each city In Model.Cities
        @<option value="@city.Value"
                 @(If(city.Value = Model.CityId, "selected", ""))
                 data-geo-lat="@city.Lat"
                 data-geo-lng="@city.Lng"
                 data-geo-zoom="@city.Zoom">
            @city.Text
        </option>
    Next
</select>

Assuming Model.Cities is a collection of items that expose each of those properties. Then you should be all set.

If you want reusability, consider making it an editor template for anything that is an Enumerable of Cities

Solution 2

You'll have to extend SelectListItem, and then extend DropDownListFor to use the extended SelectListItem.

Have a look at this solution:

Adding html class tag under <option> in Html.DropDownList

Solution 3

Here's how I ended up doing it without an extension but still enabling unobtrusive validation to continue to work & be bound to a ViewModel property.

Created an Html Helper to get the validation attributes as a string:

    public static IHtmlString GetUnobtrusiveValidationAttributesFor<TModel, TProperty>(this HtmlHelper<TModel> html, Expression<Func<TModel, TProperty>> propertySelector)
    {
        string propertyName = html.NameFor(propertySelector).ToString();
        ModelMetadata metaData = ModelMetadata.FromLambdaExpression(propertySelector, html.ViewData);
        IDictionary<string, object> attributeCollection = html.GetUnobtrusiveValidationAttributes(propertyName, metaData);

        return html.Raw(String.Join(" ", attributeCollection.Select(kvp => kvp.Key + "=\"" + kvp.Value.ToString() + "\"")));
    }

Use this helper in a select list in the view:

<select name="@Html.NameFor(m => m.CityId)" id="@Html.IdFor(m => m.CityId)"
    @Html.GetUnobtrusiveValidationAttributesFor(m => m.CityId)
    class="location_city_input">
    @foreach(var city in Model.Cities)
    {
        <option value="@city.Id.ToString()" @(city.Id == Model.CityId ? "selected" : "") 
            data-geo-lat="@city.Lat" data-geo-lng="@city.Lng" data-geo-zoom="@city.Zoom">
            @city.Name
        </option>
    }
</select>

This would output something like this:

<select id="CityId" name="CityId" 
    data-val-required="The SelectedTaxRateID field is required." data-val="true" 
    class="location_city_input">
    <option value="1" selected data-geo-lat="-34.883611" data-geo-lng="-56.181944" data-geo-zoom="13">Montevideo</option>               
    <option value="41" data-geo-lat="-34.816667" data-geo-lng="-55.95" data-geo-zoom="13">Ciudad de la Costa</option>
</select>

I will leave the conditional data- attributes up to you since those are just a matter of forming the appropriate Razor expressions.

Solution 4

MVC when it converts object names to attribute names, it relaces "_" with "-", so its:

@Html.DropDownList(a=>a.websiteid, Model.GetItems, new{ data_rel="selected" })

NOT MY ANSWER, ANSWER CREDIT GOES TO About bruce (sqlwork.com) from the ASP>NET Forums.

How can I add data-rel="selected" attribute into dropdownlistfor htmlAttributes?

JUST WANTED TO HELP OUT AS THIS SAVED ME FROM CODING A HACK! ENJOY.

Solution 5

I had a similar requirement, I created a extension. Hope it helps for the ones who wants to create an extension.

/*cs file*/
/*This contains your information with List<vmListItem>*/
public class vmListItem
{
   public int Id { get; set; }
   public string Name { get; set; }
   public string Tag { get; set; }
}

/*This contains the attributes in select, using List<vmAttribute>. Check cshtml */
public class vmAttribute
{
   public string Key { get; set; }
   public string Value { get; set; }
}

    /// <summary>
    /// Creates a dropdownlist using a list with data attributes included
    /// </summary>
    /// <param name="helper"></param>
    /// <param name="id">id and name of the select</param>
    /// <param name="attributes">list of attrs for select</param>
    /// <param name="items"><list of options/param>
    /// <param name="idSelected">id selected in option</param>
    /// <param name="tagName">data-tagName you can choose the name of your tag</param>
    /// <param name="textHeader">first option in select</param>
    /// <returns></returns>
    public static MvcHtmlString DropDownListForWithTag(this HtmlHelper helper, string id, List<vmAttribute> attributes, List<vmListItem> items, int idSelected, string tagName = "tag", string textHeader= "")
    {
        var select = new TagBuilder("select");
        select.GenerateId(id);
        select.MergeAttribute("name", id);
        foreach (vmAttribute att in atributos) select.MergeAttribute(att.Key, att.Value);

        TagBuilder headerOption = new TagBuilder("option");
        headerOption .MergeAttribute("value", null);
        headerOption .InnerHtml = textHeader;
        select.InnerHtml += headerOption ;

        foreach(var item in items)
        {                
            TagBuilder option = new TagBuilder("option");
            option.MergeAttribute("value", item.Id.ToString());
            option.MergeAttribute("data-" + tagName, item.Tag);
            if (idSelected == item.Id) option.MergeAttribute("selected", "selected");
            option.InnerHtml = item.Name;

            select.InnerHtml += option.ToString();
        }

        return new MvcHtmlString(select.ToString());
    }

/*cshtml file*/
@Html.DropDownListForWithTag("MovimientoBienMotivoId", new List<vmAttribute> {
                        new vmAttribute("class", "form-control"),
                        new vmAttribute("data-val", "true"),
                        new vmAttribute("data-val-required", "El campo Motivo es obligatorio"),
                        new vmAttribute("onchange", "movValidarCambioMotivo()"),
                    }, (List<vmListItem>)ViewBag.MovimientoBienMotivoId, Model.MovimientoBienMotivoId, "codigo", "Seleccione")
                    @Html.ValidationMessageFor(model => model.ColumnId, "", new { @class = "text-danger" })


/*html results*/

enter image description here

Share:
63,873

Related videos on Youtube

Bart Calixto
Author by

Bart Calixto

Self credited role: Full stack developer. Passionate about UX and user-centered design. Flavor: The stack of beauty MVC, Web Api, Knockout, Vanilla JavaScript, Entity Framework, SignalR. User first, mobile first, cloud first. "There are only 10 types of developers in the world: those who understand binary, and those who don't."

Updated on July 05, 2022

Comments

  • Bart Calixto
    Bart Calixto almost 2 years

    Is there anyway to have a SelectList prepopulated on ViewModel with data-attributes ?

    I want to do

    @Html.DropdownListFor(m=> m.CityId, Model.Cities);
    

    so it generates code like :

    <select id="City" class="location_city_input" name="City">
        <option data-geo-lat="-32.522779" data-geo-lng="-55.765835" data-geo-zoom="6" />
        <option data-geo-lat="-34.883611" data-geo-lng="-56.181944" data-geo-zoom="13" data-geo-name="Montevideo" data-child=".state1" value="1">Montevideo</option>               
        <option data-geo-lat="-34.816667" data-geo-lng="-55.95" data-geo-zoom="13" data-geo-name="Canelones, Ciudad de la Costa" data-child=".state41" value="41">Ciudad de la Costa</option>
    </select>
    
  • Bart Calixto
    Bart Calixto almost 11 years
    this looks like the correct answer but seems kinda ugly. I rather write the select myself than extending SelectListItem and DropDownListFor I think. I'm not sure.
  • ataravati
    ataravati almost 11 years
    I don't know why you think it's ugly, but it seems more logical to me. Each SelectListItem represents an option tag in the final html, and what you need to do is to add custom html attributes to the option tag (SelectListItem), so it only makes sense to extend the SelectListItem.
  • Bart Calixto
    Bart Calixto almost 11 years
    i think it's ugly from the mvc framework perspective. but the solution is exactly how to deal with.
  • drogon
    drogon almost 11 years
    Agreed, quite nasty just to add some simple attributes :(
  • Bart Calixto
    Bart Calixto over 9 years
    Interesting... I didn't know about html.namefor etc. I'll give it a try
  • ChandlerPelhams
    ChandlerPelhams over 9 years
    Great advice for adding real easy flexibility to your html.
  • Diego
    Diego almost 9 years
    Since razor2 you can simple do selected="@city.Value == Model.CityId" and it will generate the right markup (either selected="selected" or nothing)
  • Aleksander Bethke
    Aleksander Bethke over 8 years
    Really ugly solution. Has it been covered in the latest (5.2) version of MVC or you still heave to write custom code for that? Thanks
  • skeletank
    skeletank almost 8 years
    How do you make this still work with validation? My select does not correctly highlight in red anymore when there is a validation error.
  • Tawab Wakil
    Tawab Wakil over 7 years
    The code posted by @Diego did not work until I added parentheses: selected="@(city.Value == Model.CityId)"
  • Diego
    Diego over 7 years
    @TawabWakil, that sounds about right. I didn't run the code, I simply commented by knowing the theory.
  • Harry
    Harry almost 6 years
    Thank you best solution of all. I customized this extension to suit my needs.
  • xr280xr
    xr280xr over 5 years
    @skeletank that is why "Not everything has to be written with extension method in .NET code" is not a simple solution. Luckily in MVC 4 and above, they've provided a way to get the unobtrusive validation attributes (Html.GetUnobtrusiveValidationAttributes), but it's not pretty. Look at this answer for an in-liner, or you could write an HTML helper for it to make it easier to read: stackoverflow.com/a/37748259/263832
  • ParoX
    ParoX over 5 years
    This only added the data-rel to the select not to each option
  • spaark
    spaark about 5 years
    Great to know, although it doesn't add the attributes as wanted (like ParoX mentioned).
  • David Liang
    David Liang over 4 years
    @skeletank: to make this work with jquery validation, you need to add data-val="true" and data-val-required="@Html.DisplayNameFor(...) field is required." on the <select />.