web-dev-qa-db-fra.com

Ajout d'une balise de classe html sous <option> dans Html.DropDownList

Je cherchais des réponses sur la façon d'ajouter une balise de classe HTML sur mon html.dropdownlist. voici le code

<%: Html.DropDownList("PackageId", new SelectList(ViewData["Packages"] as IEnumerable, "PackageId", "Name", Model.PackageId))%>

Je souhaite ajouter des classes pour les options sous l'élément select afin que je puisse utiliser cette sélection chaînée:

<select id="category">
  <option value="1">One</option>
  <option value="2">Two</option>
</select>
<select id="package">
  <option value="1" class="1">One - package1</option>
  <option value="2" class="1">One - package2</option>
  <option value="3" class="2">Two - package1</option>
  <option value="4" class="2">Two - package2</option>
</select>

$("#series").chained("#mark");
35
paul

Je l'ai fait pour la méthode d'extension DropDownlistFor et non pour DropDownList que vous utilisez, mais vous pouvez probablement le découvrir vous-même. Ce matériel est principalement copié/collé depuis les sources MVC. Vous pouvez trouver les sources ici

public class ExtendedSelectListItem : SelectListItem
{
    public object htmlAttributes { get; set; }
}

public static partial class HtmlHelperExtensions
{
    public static MvcHtmlString ExtendedDropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<ExtendedSelectListItem> selectList, string optionLabel, object htmlAttributes)
    {
        return SelectInternal(htmlHelper, optionLabel, ExpressionHelper.GetExpressionText(expression), selectList, false /* allowMultiple */, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
    }

    private static MvcHtmlString SelectInternal(this HtmlHelper htmlHelper, string optionLabel, string name, IEnumerable<ExtendedSelectListItem> selectList, bool allowMultiple, IDictionary<string, object> htmlAttributes)
    {
        string fullName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name);
        if (String.IsNullOrEmpty(fullName))
            throw new ArgumentException("No name");

        if (selectList == null)
            throw new ArgumentException("No selectlist");

        object defaultValue = (allowMultiple) ? GetModelStateValue(htmlHelper, fullName, typeof(string[])) : GetModelStateValue(htmlHelper, fullName, typeof(string));

        // If we haven't already used ViewData to get the entire list of items then we need to
        // use the ViewData-supplied value before using the parameter-supplied value.
        if (defaultValue == null)
            defaultValue = htmlHelper.ViewData.Eval(fullName);

        if (defaultValue != null)
        {
            IEnumerable defaultValues = (allowMultiple) ? defaultValue as IEnumerable : new[] { defaultValue };
            IEnumerable<string> values = from object value in defaultValues select Convert.ToString(value, CultureInfo.CurrentCulture);
            HashSet<string> selectedValues = new HashSet<string>(values, StringComparer.OrdinalIgnoreCase);
            List<ExtendedSelectListItem> newSelectList = new List<ExtendedSelectListItem>();

            foreach (ExtendedSelectListItem item in selectList)
            {
                item.Selected = (item.Value != null) ? selectedValues.Contains(item.Value) : selectedValues.Contains(item.Text);
                newSelectList.Add(item);
            }
            selectList = newSelectList;
        }

        // Convert each ListItem to an <option> tag
        StringBuilder listItemBuilder = new StringBuilder();

        // Make optionLabel the first item that gets rendered.
        if (optionLabel != null)
            listItemBuilder.Append(ListItemToOption(new ExtendedSelectListItem() { Text = optionLabel, Value = String.Empty, Selected = false }));

        foreach (ExtendedSelectListItem item in selectList)
        {
            listItemBuilder.Append(ListItemToOption(item));
        }

        TagBuilder tagBuilder = new TagBuilder("select")
        {
            InnerHtml = listItemBuilder.ToString()
        };
        tagBuilder.MergeAttributes(htmlAttributes);
        tagBuilder.MergeAttribute("name", fullName, true /* replaceExisting */);
        tagBuilder.GenerateId(fullName);
        if (allowMultiple)
            tagBuilder.MergeAttribute("multiple", "multiple");

        // If there are any errors for a named field, we add the css attribute.
        ModelState modelState;
        if (htmlHelper.ViewData.ModelState.TryGetValue(fullName, out modelState))
        {
            if (modelState.Errors.Count > 0)
            {
                tagBuilder.AddCssClass(HtmlHelper.ValidationInputCssClassName);
            }
        }

        tagBuilder.MergeAttributes(htmlHelper.GetUnobtrusiveValidationAttributes(name));

        return MvcHtmlString.Create(tagBuilder.ToString(TagRenderMode.Normal));
    }

    internal static string ListItemToOption(ExtendedSelectListItem item)
    {
        TagBuilder builder = new TagBuilder("option")
        {
            InnerHtml = HttpUtility.HtmlEncode(item.Text)
        };
        if (item.Value != null)
        {
            builder.Attributes["value"] = item.Value;
        }
        if (item.Selected)
        {
            builder.Attributes["selected"] = "selected";
        }
        builder.MergeAttributes(HtmlHelper.AnonymousObjectToHtmlAttributes(item.htmlAttributes));
        return builder.ToString(TagRenderMode.Normal);
    }
}
51
John Landheer

Voici une version légèrement améliorée de la solution @ john-landheer.

Les choses se sont améliorées:

  • problème avec GetModelStateValue() résolu
  • DropDownList() méthode d'extension ajoutée
  • les attributs de validation discrets seront rendus comme ils le devraient

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Globalization;
    using System.Linq;
    using System.Linq.Expressions;
    using System.Text;
    using System.Web;
    using System.Web.Mvc;
    
    namespace App.Infrastructure.Helpers
    {
        public class ExtendedSelectListItem : SelectListItem
        {
            public object HtmlAttributes { get; set; }
        }
    
        public static class ExtendedSelectExtensions
        {
            internal static object GetModelStateValue(this HtmlHelper htmlHelper, string key, Type destinationType)
            {
                System.Web.Mvc.ModelState modelState;
                if (htmlHelper.ViewData.ModelState.TryGetValue(key, out modelState))
                {
                    if (modelState.Value != null)
                    {
                        return modelState.Value.ConvertTo(destinationType, null /* culture */);
                    }
                }
                return null;
            }
    
            public static MvcHtmlString ExtendedDropDownList(this HtmlHelper htmlHelper, string name, IEnumerable<ExtendedSelectListItem> selectList)
            {
                return ExtendedDropDownList(htmlHelper, name, selectList, (string)null, (IDictionary<string, object>)null);
            }
    
            public static MvcHtmlString ExtendedDropDownList(this HtmlHelper htmlHelper, string name, IEnumerable<ExtendedSelectListItem> selectList, string optionLabel, IDictionary<string, object> htmlAttributes)
            {
                return ExtendedDropDownListHelper(htmlHelper, null, name, selectList, optionLabel, htmlAttributes);
            }
    
            public static MvcHtmlString ExtendedDropDownListHelper(this HtmlHelper htmlHelper, ModelMetadata metadata, string expression, IEnumerable<ExtendedSelectListItem> selectList, string optionLabel, IDictionary<string, object> htmlAttributes)
            {
                return SelectInternal(htmlHelper, metadata, optionLabel, expression, selectList, false, htmlAttributes);
            }
    
            public static MvcHtmlString ExtendedDropDownListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper,
                Expression<Func<TModel, TProperty>> expression, IEnumerable<ExtendedSelectListItem> selectList,
                string optionLabel, object htmlAttributes)
            {
                if (expression == null)
                    throw new ArgumentNullException("expression");
                ModelMetadata metadata = ModelMetadata.FromLambdaExpression<TModel, TProperty>(expression, htmlHelper.ViewData);
                return SelectInternal(htmlHelper, metadata, optionLabel, ExpressionHelper.GetExpressionText(expression), selectList,
                    false /* allowMultiple */, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
            }
    
            private static MvcHtmlString SelectInternal(this HtmlHelper htmlHelper, ModelMetadata metadata, string optionLabel, string name,
                IEnumerable<ExtendedSelectListItem> selectList, bool allowMultiple,
                IDictionary<string, object> htmlAttributes)
            {
                string fullName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name);
                if (String.IsNullOrEmpty(fullName))
                    throw new ArgumentException("No name");
    
                if (selectList == null)
                    throw new ArgumentException("No selectlist");
    
                object defaultValue = (allowMultiple)
                    ? htmlHelper.GetModelStateValue(fullName, typeof(string[]))
                    : htmlHelper.GetModelStateValue(fullName, typeof(string));
    
                // If we haven't already used ViewData to get the entire list of items then we need to
                // use the ViewData-supplied value before using the parameter-supplied value.
                if (defaultValue == null)
                    defaultValue = htmlHelper.ViewData.Eval(fullName);
    
                if (defaultValue != null)
                {
                    IEnumerable defaultValues = (allowMultiple) ? defaultValue as IEnumerable : new[] { defaultValue };
                    IEnumerable<string> values = from object value in defaultValues
                                                 select Convert.ToString(value, CultureInfo.CurrentCulture);
                    HashSet<string> selectedValues = new HashSet<string>(values, StringComparer.OrdinalIgnoreCase);
                    List<ExtendedSelectListItem> newSelectList = new List<ExtendedSelectListItem>();
    
                    foreach (ExtendedSelectListItem item in selectList)
                    {
                        item.Selected = (item.Value != null)
                            ? selectedValues.Contains(item.Value)
                            : selectedValues.Contains(item.Text);
                        newSelectList.Add(item);
                    }
                    selectList = newSelectList;
                }
    
                // Convert each ListItem to an <option> tag
                StringBuilder listItemBuilder = new StringBuilder();
    
                // Make optionLabel the first item that gets rendered.
                if (optionLabel != null)
                    listItemBuilder.Append(
                        ListItemToOption(new ExtendedSelectListItem()
                        {
                            Text = optionLabel,
                            Value = String.Empty,
                            Selected = false
                        }));
    
                foreach (ExtendedSelectListItem item in selectList)
                {
                    listItemBuilder.Append(ListItemToOption(item));
                }
    
                TagBuilder tagBuilder = new TagBuilder("select")
                {
                    InnerHtml = listItemBuilder.ToString()
                };
                tagBuilder.MergeAttributes(htmlAttributes);
                tagBuilder.MergeAttribute("name", fullName, true /* replaceExisting */);
                tagBuilder.GenerateId(fullName);
                if (allowMultiple)
                    tagBuilder.MergeAttribute("multiple", "multiple");
    
                // If there are any errors for a named field, we add the css attribute.
                System.Web.Mvc.ModelState modelState;
                if (htmlHelper.ViewData.ModelState.TryGetValue(fullName, out modelState))
                {
                    if (modelState.Errors.Count > 0)
                    {
                        tagBuilder.AddCssClass(HtmlHelper.ValidationInputCssClassName);
                    }
                }
    
                tagBuilder.MergeAttributes(htmlHelper.GetUnobtrusiveValidationAttributes(fullName, metadata));
    
                return MvcHtmlString.Create(tagBuilder.ToString(TagRenderMode.Normal));
            }
    
            internal static string ListItemToOption(ExtendedSelectListItem item)
            {
                TagBuilder builder = new TagBuilder("option")
                {
                    InnerHtml = HttpUtility.HtmlEncode(item.Text)
                };
                if (item.Value != null)
                {
                    builder.Attributes["value"] = item.Value;
                }
                if (item.Selected)
                {
                    builder.Attributes["selected"] = "selected";
                }
                builder.MergeAttributes(HtmlHelper.AnonymousObjectToHtmlAttributes(item.HtmlAttributes));
                return builder.ToString(TagRenderMode.Normal);
            }
    
        }
    }
    
13
Alexander Puchkov

Cela n'est pas possible avec l'assistant DropDownList intégré à ASP.NET MVC. En conséquence, vous devrez écrire votre propre aide si vous devez le faire. Vous pouvez consulter le code source d'ASP.NET MVC, qui utilise TagBuilder pour générer les options, et vous pouvez ajouter tous les attributs dans votre implémentation personnalisée. Une autre solution moins élégante consiste à parcourir manuellement le jeu de données dans la vue et à générer des éléments d’option individuels.

5
Darin Dimitrov

La première chose qui me vient à l’esprit est JQuery ici. Vous pouvez le faire facilement avec le code suivant: 

$("#bla").find("option").addClass("poo");
2
tugberk

J'ai écrit un wrapper qui modifie simplement le code HTML:

    public static MvcHtmlString DisableFirstItem(MvcHtmlString htmlString)
    {
        return new MvcHtmlString(
            htmlString.ToString()
            .Replace("<option value=\"Unknown\">", 
                     "<option disabled value=\"Unknown\">")
        );
    }

et puis j'ai enveloppé mon DropDownListFor avec cette fonction d'assistance:

        @Html.Raw(MyHtmlHelpers.DisableFirstItem(

          Html.DropDownListFor(m => m.Instrument,
            new SelectList(ReflectionHelpers.GenerateEnumDictionary<OrderInstrument>(true), "Key", "Value", Model.Instrument),
            new { @class = "form-control" })

        ))

Vous pouvez évidemment rendre la fonction d'aide plus sophistiquée si vous le souhaitez.

0
Brian Rice