TOC

This article has been localized into Spanish by the community.

Tag Helpers:

Tag Helper para Select

El "Tag Helper" para "Select" te permitirá crear fácilmente elementos SELECT de HTML basados en tus datos. Este es uno de los "Tag Helpers" más avanzado y con más funcionalidad que el resto, pero como verás, eso solo significa que te ahorrará más digitación cuando crees tus Vistas! ya que el "Tag Helper" hace un poco más de trabajo por ti, los ejemplos en éste artículo son un poco más complejos comparados a los anteriores.

Primero que todo, hablemos sobre los que hace el elemento SELECT. Aquí un ejemplo HTML puro:

<select>
    <option value="1">The Godfather</option>
    <option value="2">Forrest Gump</option>
    <option value="3">Fight Club</option>
</select>

En el navegador esto se verá de la siguiente manera:

Como puedes ver, el elemento SELECT mostrará una o varias opciones en una lista (una lista desplegable por defecto), permitiendo la selección de una opción. Las opciones son proporcionadas por el elemento OPTION y cada una de ellas puede tener un valor (p.ej. un ID), así como una etiqueta de texto (en este caso el título de una película).

Sin embargo, no hay necesidad de programar esas etiquetas OPTION - podemos generarlas automáticamente con una fuente de datos (generalmente una base de datos) con el "Tag Helper" Select.

Los atributos "for" e "Items"

Al igual que con varios otros "Tag Helpers", el "Tag Helper" Select posee el atributo asp-for, el que permite enlazar su valor a una propiedad específica en el Modelo. Además, está disponible un atributo llamado asp-items, el que permite proporcionar una fuente de datos para el elemento SELECT.

El atributo asp-for necesita una propiedad en el Modelo a la que enlazarse y asp-items necesita una lista de opciones. Mientras que ambas cosas pueden venir del mismo Modelo, es a menudo más práctico el crear un ViewModel específico para este propósito, el que se combinará con el Modelo en el que trabajas y la lista de opciones. Un ejemplo de esto puede ser un Formulario para la edición de usuarios, donde debe existir la posibilidad de editar los datos como nombre, e-mail, etc., además de permitir al usuario el seleccionar el país en el que vive. Esta lista de países vendrá de otra fuente, p.ej. una base de datos o una lista de países escogidos arbitrariamente. En este caso, podrás crear un ViewModel como el siguiente:

public class EditUserViewModel
{
	public WebUser User { get; set; }
	
	public List<string> Countries { get; set; }
}

La clase WebUser se verá como sigue:

public class WebUser
{		
	public string FirstName { get; set; }

	public string LastName { get; set; }

	public string Country { get; set; }
}

Ahora cuando se requiere mostrar la Vista de edición al usuario, se debería crear el ViewModel y suministrarlo con el objeto WebUser y la lista de países. Obviamente, ambas cosas vendrán de otra fuente, p.ej. una base de datos, pero a modo de ilustración, éstos serán creados sobre la marcha. Así es como se ve el Controlador:

public IActionResult EditUser()
{
	EditUserViewModel viewModel = new EditUserViewModel();
	viewModel.User = new WebUser()
	{
		FirstName = "John",
		LastName = "Doe",
		Country = "USA"
	};
	viewModel.Countries = new List<string>()
	{
		"USA",
		"Great Britain",
		"Germany"
	};
	return View(viewModel);
}

Hecho esto, finalmente podremos ver la Vista donde se usará el "Tag Helper":

@model HelloMVCWorld.ViewModels.EditUserViewModel

<form asp-action="UpdateUser" method="post">
    <input asp-for="User.FirstName" placeholder="First name" />
    <input asp-for="User.LastName" placeholder="Last name" />

    <select asp-items="@(new SelectList(Model.Countries))" asp-for="User.Country"></select>
    
    <input type="submit" value="Save" />
</form>

Lo importante aquí es, por su puesto, la etiqueta SELECT, donde se ha usado el "Tag Helper" para generar la lista desplegable. Pon especial atención al atributo asp-items, donde se creó un nuevo SelectList basado en la lista de países. La razón es que el atributo asp-items espera que la lista proporcionada contenga instancias de la clase SelectItem - esta versión del constructor de SelectList automáticamente crea estas instancias, basadas en la fuente proporcionada. El código HTML generado para este elemento se verá como sigue:

<select id="User_Country" name="User.Country">
	<option selected="selected">USA</option>
	<option>Great Britain</option>
	<option>Germany</option>
</select>

El valor "USA" ha sido pre seleccionado (con el atributo "selected") porque el Modelo generado en el Controlador especifica éste como el país actual - todo esto es manejado con esta única línea de código HTML/Razor, gracias al "Tag Helper"!

Listas con Claves/Valores

Una cosa que podrías preguntarte sobre el ejemplo de arriba: Es que no se han proporcionado valores para la etiqueta OPTION, lo que significa que el texto del item seleccionado también será usado como valor cuando se envíe el Formulario. Esto es por que generamos las etiquetas "option" par una simple lista de strings (en este caso, nombre de países), para hacer el ejemplo inicial un poco mas simple. Sin embargo, en la mayoría de las situaciones, las listas se generarán en base a las combinaciones de claves y valores. Por ejemplo, los países podrían venir de una base de datos, en donde se usa un número como identificador y luego un campo Titulo/Nombre para el nombre del país. Afortunadamente, esto es también posible y muy fácil de lograr.

Digamos que se tiene una clase llamada Country, que consiste en dos propiedades: Id y Name. Cuando se crea el ViewModel, podrías entonces cargar los países desde una base de datos u otra fuente de datos. En la Vista se usa un constructor alternativo para SelectList, permitiendo especificar que propiedades del objeto proveedor se usaran para el valor y texto, como sigue:

<select asp-items="@(new SelectList(Model.Countries, "Id", "Name"))" asp-for="User.CountryId"></select><br /><br />

Simple y flexible - tal y como nos gusta!

Trabajando con enumeraciones

Otro escenario muy común para el "Tag Helper" de Select, es en combinación con enumeraciones (enum). Una enumeración es una lista de posibles opciones, definidas en código, con cada opción correspondiendo a un número. Esto es muy útil en un montón de situaciones y encontraras montones de enumeraciones en Framework. Un ejemplo de esto es la enumeración DayOfWeek, usada por la propiedad del mismo nombre en la estructura DateTime. Se define como sigue:

public enum DayOfWeek
{
	Sunday = 0,
	Monday = 1,
	Tuesday = 2,
	Wednesday = 3,
	Thursday = 4,
	Friday = 5,
	Saturday = 6
}

Y puedes fácilmente definir tus propias enumeraciones, como las siguientes:

public enum WebUserStatus
{
	Unknown,
	Active,
	Deleted
}

A no ser que se les asigne un número específico, el primer miembro será el 0, el segundo el 1 y así sucesivamente.

Con la enumeración WebUserStatus anteriormente definida, trataremos de usarla en un elemento SELECT. Para esto existe un método de ayuda en la clase HtmlHelper, que podremos usar para este propósito:

<select asp-items="@(Html.GetEnumSelectList<HelloMVCWorld.Models.WebUserStatus>())" asp-for="Status"></select>

El resultado será una lista desplegable con las opciones de la enumeración WebUserStatus!

Las opciones de nuestro enumerador son bastante auto explicativas, pero como las especificaciones del lenguaje no permiten caracteres como espacios en blanco y varios caracteres especiales, a menudo tendrás que crear enumeradores con nombres que no son muy fáciles de leer, como estos:

public enum WebUserValidationStatus
{
	Unknown,		
	NotVerifiedYet,
	VerifiedByMail,
	VerifiedByPhone
}

Estos aún tienen sentido, pero so se verán correctos para los que no son programadores. Por fortuna, se pueden agregar fácilmente versiones legibles para todos o algunos de ellos. Si usas la anotación de datos Display, estos valores serán usados en vez de la etiqueta del enumerador. Aquí hay un ejemplo:

public enum WebUserValidationStatus
{
	Unknown,		
	[Display(Name = "Not verified yet")]
	NotVerifiedYet,
	[Display(Name = "Verified by e-mail")]
	VerifiedByMail,
	[Display(Name = "Verified by phone")]
	VerifiedByPhone
}

Y así es como se verían:

Trabajando con grupos

El elemento nativo HTML Select admite la agrupación de datos, lo que básicamente solo significa que puedes agregar elementos que funcionan como encabezados en lugar de elementos reales, haciendo que los siguientes elementos aparezcan como parte de este grupo. El Tag Helper para Select también admite este comportamiento, pero requiere que le entregues un conjunto de elementos agrupados en lugar de cualquier tipo de objetos como vimos en ejemplos anteriores. Para ello, normalmente crearas un ViewModel para contener los elementos agrupados:

using Microsoft.AspNetCore.Mvc.Rendering;
using System.Collections.Generic;

namespace HelloMVCWorld.ViewModels
{
	public class GroupedMoviesViewModel
	{
		public GroupedMoviesViewModel()
		{
			this.Movies = new List<SelectListItem>();
		}

		public List<SelectListItem> Movies { get; set; }
	}
}

Luego, cuando se crea el ViewModel, deberías crear una instancia de la clase SelectListItem para contener los items, y crear una instancia de la clase SelectListGroup para cada grupo, asignando estos a SelectListItem. Suena más complicado de lo que es, como podrás ver:

public IActionResult SelectGroups()
{
	GroupedMoviesViewModel viewModel = new GroupedMoviesViewModel();

	SelectListGroup dramaMovies = new SelectListGroup() { Name = "Dramas" };
	viewModel.Movies.Add(new SelectListItem()
	{
		Group = dramaMovies,
		Text = "Forrest Gump"
	});
	viewModel.Movies.Add(new SelectListItem()
	{
		Group = dramaMovies,
		Text = "Fight Club"
	});

	SelectListGroup comedyMovies = new SelectListGroup() { Name = "Comedies" };
	viewModel.Movies.Add(new SelectListItem()
	{
		Group = comedyMovies,
		Text = "Anchorman: The Legend of Ron Burgundy"
	});
	viewModel.Movies.Add(new SelectListItem()
	{
		Group = comedyMovies,
		Text = "Step Brothers"
	});

	return View(viewModel);
}

El resultado será como lo que sigue:

Como se puede ver en el HTML resultante, los grupos son creados con la etiqueta OPTGROUP, en vez de la etiqueta OPTION usada para elementos regulares:

<select>
	<optgroup label="Dramas">
		<option>Forrest Gump</option>
		<option>Fight Club</option>
	</optgroup>
	<optgroup label="Comedies">
		<option>Anchorman: The Legend of Ron Burgundy</option>
		<option>Step Brothers</option>
	</optgroup>
</select>

Trabajando con listas de selección múltiples

En algunas situaciones, quieres que el usuario pueda seleccionar más de un item de la lista al mismo tiempo. Esto no es posible en una lista desplegable, que es el tipo por defecto para el elemento Select, pero de hecho si es soportado. Si se usa el atributo "multiple", la etiqueta SELECT se mostrará como una listbox, permitiéndote seleccionar multiples items:

Afortunadamente, el "Tag Helper" para Select soporta esto también - de hecho, usará automáticamente una lista de selección múltiple si la propiedad usada en el atributo asp-for es del tipo IEnumerable, p.ej. una Lista. Aquí un ejemplo de un ViewModel que entrega un conjunto de películas a la Vista, mientras acepta selección múltiple en la propiedad FavoriteMovieIds:

public class FavoriteMoviesViewModel
{
	public List<Movie> Movies { get; set; }

	public List<int> FavoriteMovieIds { get; set; }
}

Usarlo en la Vista es tan simple como esto:

<select asp-for="FavoriteMovieIds" asp-items="@(new SelectList(Model.Movies, "Id", "Title"))"></select>

Esto resultará en la lista que se ve en el screenshot mas arriba, El HTML se ve mucho más como antes, agregando el atributo multiple:

<select id="FavoriteMovieIds" multiple="multiple" name="FavoriteMovieIds">
	<option value="1">The Godfather</option>
	<option value="2">Forrest Gump</option>
	<option value="3">Fight Club</option>
</select>

Sumario

El elemento SELECT es uno de los más complejos elementos HTML, con un montón de posibilidades, es por esto que el "Tag Helper" para Select es un poco más complejo de usar, como se vio en los ejemplos más arriba. Sin embargo, esto también significa que puede ahorrarte mas tiempo del usual!


This article has been fully translated into the following languages: Is your preferred language not on the list? Click here to help us translate this article into your language!