SelectListItem with data-attributes
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*/
Related videos on Youtube
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, 2022Comments
-
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 almost 11 yearsthis 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 almost 11 yearsI 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 almost 11 yearsi think it's ugly from the mvc framework perspective. but the solution is exactly how to deal with.
-
drogon almost 11 yearsAgreed, quite nasty just to add some simple attributes :(
-
Bart Calixto over 9 yearsInteresting... I didn't know about html.namefor etc. I'll give it a try
-
ChandlerPelhams over 9 yearsGreat advice for adding real easy flexibility to your html.
-
Diego almost 9 yearsSince razor2 you can simple do
selected="@city.Value == Model.CityId"
and it will generate the right markup (eitherselected="selected"
or nothing) -
Aleksander Bethke over 8 yearsReally 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 almost 8 yearsHow 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 over 7 yearsThe code posted by @Diego did not work until I added parentheses:
selected="@(city.Value == Model.CityId)"
-
Diego over 7 years@TawabWakil, that sounds about right. I didn't run the code, I simply commented by knowing the theory.
-
Harry almost 6 yearsThank you best solution of all. I customized this extension to suit my needs.
-
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 over 5 yearsThis only added the data-rel to the
select
not to eachoption
-
spaark about 5 yearsGreat to know, although it doesn't add the attributes as wanted (like ParoX mentioned).
-
David Liang over 4 years@skeletank: to make this work with jquery validation, you need to add
data-val="true"
anddata-val-required="@Html.DisplayNameFor(...) field is required."
on the <select />.