tag net mvc microsoft asp asp.net-mvc-3 razor html.dropdownlistfor

asp.net mvc 3 - net - Añadir atributo para seleccionar la opción de lista



microsoft asp.net mvc 3 (5)

Tengo una lista de elementos en una lista desplegable dentro de una vista de Razor. En la base de datos, cada elemento tiene 3 valores asociados: el ID de la base de datos, el nombre corto (para mostrar) y el nombre largo (para pasar a un servicio). La lista desplegable debe mostrar el nombre corto, por lo que estoy rellenando la lista desplegable con el ID de la base de datos como el valor y el nombre corto como el texto.

Sin embargo, cuando un usuario selecciona un elemento, necesito pasar el nombre largo como parámetro de consulta a un servicio de búsqueda utilizando jQuery, por ejemplo, cuando se selecciona Cortina, "Ford Cortina 1979 Blue" debe pasarse al servicio. Mi primer pensamiento es almacenar el nombre largo como un atributo de guión de datos, pero me pregunto si hay una mejor manera. Asi que

  • ¿Cómo almaceno los 3 valores en la lista desplegable?
  • Si utilizo los atributos del guión de datos, ¿cómo incorporo todos los valores de LONG_NAME a Html.DropDownListFor o los agrego de alguna manera a la lista desplegable?

DB:

CARID SHORT_NAME LONG_NAME 1 Viper Dodge Viper 1982 2 Boxster Porsche Boxster 2009 Black 3 Cortina Ford Cortina 1979 Blue

Controlador auxiliar para crear el desplegable:

public static IEnumerable<SelectListItem> GetSelectList(this IEFRepository repository, string typeName) { var vehicle = repository.TypeTypes.FirstOrDefault(t => t.Name.ToUpper() == typeName); if (vehicle != null) { var carList = vehicle.SubTypes.ToList().OrderBy(s => s.Name); var selectList = new SelectList(subTypeList, "SubTypeID", "Name"); return selectList; } }

Aquí está el código que utilizo para crear el menú desplegable:

<div class="editor-field"> @Html.DropDownListFor(model => model.CarID, new SelectList(ViewBag.Cars, "Value", "Text", "1")) @Html.ValidationMessageFor(model => model.CarShortName) </div>

Aquí está la salida:

<select id="CarID" name="CarID" data-val="true" data-val-number="The field CarID must be a number." data-val-required="The CarID field is required."> <option value="2">Boxster</option> <option value="3">Cortina</option> <option selected="selected" value="1">Viper</option> </select>


@nikeaa Gracias por tu código. Encontré algunos problemas con él (por ejemplo, cuando la lista de opciones está vacía, la selección no se representa correctamente; no es necesario que reemplace las opciones, solo modifíquelas; de lo contrario, se eliminarán algunos atributos de la selección) y agregué algunas Parámetros adicionales para usar completamente el poder de DropDownListFor. Aquí está mi versión del método:

public static MvcHtmlString DropDownListWithItemAttributesFor<TModel, TProperty>( this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IEnumerable<SelectListItemWithAttributes> selectList, string optionLabel, IDictionary<string, object> htmlAttributes) { if (selectList == null || !selectList.Any()) return htmlHelper.DropDownListFor(expression, selectList, optionLabel, htmlAttributes); var selectDoc = XDocument.Parse(htmlHelper.DropDownListFor(expression, selectList, optionLabel, htmlAttributes).ToString()); var options = selectDoc.Element("select").Descendants().ToArray(); for (int i = 0; i < options.Length; i++) { var option = options[i]; var attributes = selectList.ElementAt(i); foreach (var attribute in attributes.Attributes) option.SetAttributeValue(attribute.Key, attribute.Value); } return MvcHtmlString.Create(selectDoc.ToString()); }


Dentro de la acción del controlador que se supone que debe recibir el envío del formulario, puede utilizar el ID del valor seleccionado para consultar su base de datos con el fin de obtener el nombre largo y hacer lo que pretendía hacer con él.

El asistente DropDownListFor no admite la adición de atributos de data-* HTML5 a las opciones, pero incluso si lo hicieran, no se enviarán como parte de un envío de formulario estándar. Tendrá que usar javascript para enviarlos al servidor utilizando otra técnica (campos ocultos, AJAX, parámetros de cadena de consulta, ...).

Pero si por alguna razón necesita atributos adicionales en la etiqueta de opción, siempre puede escribir un ayudante personalizado.


Solo volviendo a esto ahora. Si bien la respuesta de @Nikeaa es ciertamente una solución viable, pensé que era un poco pesada, especialmente usando XDocument. Como recordatorio, estoy tratando con un TypeType (Cars) y SubType (lista de tipos de autos: Viper, Granada, Hunter, Zodiac, Wolsley 1660, etc.). TypeType también podría ser Camiones, Bicicletas, etc. Entonces, así es como lo resolví:

Agregué un método JsonResult en el controlador para devolver un objeto anónimo con las 3 propiedades que quería:

public class VehicleController : Controller { // etc. public JsonResult GetSubTypesForTypeType(string typeTypeName) { var cars = pronova2Repository.GetTypeWithSubTypes(typeTypeName); return cars == null ? Json(new object[0], JsonRequestBehavior.AllowGet) : Json(cars.SubTypes.OrderBy(s => s.Name).Select( s => new { s.SubTypeID, s.Name, s.Description }).ToArray(), JsonRequestBehavior.AllowGet); } // etc. }

Luego en js:

Rellene el menú desplegable:

// populate the cars drop down when the select list is available if ($(''select#SubTypeID'').length) { var carsSelect = $(''select#SubTypeID''); var carsList = populateCarsList("CARS"); var carsListHtml = createCarsSelectList(carsList); carsSelect.html(''''); carsSelect.append(carsListHtml); $(''#SubTypeID'').change(function (e) { clearFormData(); }); }

Llame a una función para obtener los subtipos (autos) a través de una llamada ajax:

function populateCarsList(typeTypeName) { var carsList; $.ajax({ url: ''/Vehicle/GetSubTypesForTypeType'', data: { typeTypeName: typeTypeName }, async: false }).done(function (data) { carsList = data; }).error(function (msg, url, line) { alert("Error retrieving cars from Vehicle/GetSubTypesForTypeType. Error message: " + line); }); return carsList; }

Función para crear la lista de selección con la descripción agregada como un atributo "data- *":

function createCarsSelectList(selectData) { var html = '''', len = selectData.length, selected, description; for (var i = 0; i < len; i++) { // "Viper" should be selected by default if (selectData[i].Name.toLocaleUpperCase() === "VIPER") { selected = '' selected="selected" ''; } else { selected = ''''; } // Add the description (as a "data-" attribute), some descritions are null if (selectData[i].Description != null) { description = selectData[i].Description; } else { description = ''''; } html += ''<option value="'' + selectData[i].SubTypeID + ''" data-description="'' + description + ''"'' + selected + ''>'' + selectData[i].Name + ''</option>''; } return html; }


Todos se olvidan de la forma "clásica" de resolver estos problemas: use un bucle foreach y escriba el HTML de entrada. El único inconveniente es que tiene que agregar los atributos de los atributos automáticos (como la validación, etc.), lo que dependiendo de su propósito puede no ser un gran problema.

Algo como:

<select> // add other attributes as expected @foreach(var type in Model.MyFancyTypes) { <option value="@type.SubTypeID" data-description="@type.Description" @if(ViewBag.TypeSelected == type.SubTypeID) { selected="selected" }>@type.Name</option> } </select>


Tuve una situación similar en la que necesitaba pasar un tercer valor a cada uno de los elementos de la lista para determinar la acción a realizar en una función jQuery. Aquí está mi solución (que le permitirá agregar cualquier número de atributos a cada elemento en el menú desplegable):

Primero, creé una clase SelectListItemWithAttributes de la siguiente manera:

public class SelectListItemWithAttributes : SelectListItem { public IDictionary<string, string> HtmlAttributes { get; set; } }

Esto me permite crear elementos para la lista de selección con los atributos adicionales adjuntos.

En segundo lugar, creé un método de ayuda HTML llamado DropDownListWithItemAttributesFor de la siguiente manera:

public static MvcHtmlString DropDownListWithItemAttributesFor<TModel, TValue>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TValue>> expression, IEnumerable<SelectListItemWithAttributes> selectList) { string name = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(ExpressionHelper.GetExpressionText(expression)); var selectDoc = XDocument.Parse(htmlHelper.DropDownList(name, (IEnumerable<SelectListItem>)selectList).ToString()); var options = from XElement el in selectDoc.Element("select").Descendants() select el; for (int i = 0; i < options.Count(); i++){ var option = options.ElementAt(i); var attributes = selectList.ElementAt(i); foreach (var attribute in attributes.HtmlAttributes){ option.SetAttributeValue(attribute.Key, attribute.Value); } } selectDoc.Root.ReplaceNodes(options.ToArray()); return MvcHtmlString.Create(selectDoc.ToString()); }

Esto me permite crear un menú desplegable utilizando la nueva clase SelectListWithAttributes como los atributos. Básicamente, crea el HTML para la lista desplegable, lo analiza en un documento XML y luego agrega cualquier elemento de la matriz HtmlAttributes como atributos adicionales a cada elemento del menú desplegable.

Tercero, en mi código de ViewModel tengo lo siguiente:

private List<SelectListItemWithAttributes> pDropDownDatas = null; public List<SelectListItemWithAttributes> DropDownDatas { get { var DropDownDataItems = ( from c in db.GetDropDownDataList(1, 1000) where c.AccountTypeID == this.AccountTypeID select new SelectListItemWithAttributes() { Text = c.Title, Value = c.ID.ToString(), HtmlAttributes = new Dictionary<string, string> { { "data-callback", c.RequiresCallback.ToString().ToLower() } } } ).ToList() ; DropDownDataItems.Insert(0, new SelectListItemWithAttributes() { Text = "-- Select --", Value = "", HtmlAttributes = new Dictionary<string, string> { { "data-callback", "false" } } }); return DropDownDataItems; } }

Esto crea la lista de SelectListItemsWithAttributes que finalmente completarán el menú desplegable. Esto podría estar en un controlador en lugar del modelo de vista, acabo de elegir que sea una propiedad de mi modelo de vista.

Por último, en la vista se vería así:

@Html.DropDownListWithItemAttributesFor(m => m.DropDownDataID, Model.DropDownDatas)

Esto mostrará el menú desplegable en la página utilizando la propiedad del modelo de vista que contiene la lista de SelectListItemsWithAttributes.

Construí esta solución a partir de varias soluciones que encontré en Internet, por lo que no fue todo original para mí, pero la armé en algo que funcionó para mí.

Espero que esto te ayude a resolver tu problema.