Asistentes de etiquetas en formularios de ASP.NET Core

Por Rick Anderson, N. Taylor Mullen, Dave Paquette y Jerrie Pelser

En este documento se explica cómo trabajar con formularios y se detallan los elementos HTML que se usan habitualmente en un formulario. El elemento HTML Form proporciona el mecanismo principal que las aplicaciones web usan a la hora de devolver datos al servidor. La mayor parte de este documento se centra en describir los asistentes de etiquetas y cómo pueden servir para crear formularios HTML eficaces de manera productiva. Se recomienda leer Introduction to Tag Helpers (Introducción a los asistentes de etiquetas) antes de este documento.

En muchos casos, los asistentes HTML proporcionan un enfoque alternativo a un asistente de etiquetas específico. Sin embargo, es importante reconocer que los asistentes de etiquetas no reemplazan a los asistentes HTML y no hay un asistente de etiquetas para cada asistente HTML. Si existe una alternativa del asistente de HTML, se mencionará aquí.

Asistente de etiqueta de formulario

El asistente de etiquetas Form hace lo siguiente:

  • Genera el valor del atributo HTML <FORM>action para una acción del controlador MVC o una ruta nombrada.

  • Genera un token de comprobación de solicitud oculto para evitar la falsificación de solicitudes entre sitios (cuando se usa con el [ValidateAntiForgeryToken] atributo en el método de acción HTTP Post).

  • Proporciona el atributo asp-route-<Parameter Name>, donde <Parameter Name> se agrega a los valores de ruta. Los parámetros routeValues de Html.BeginForm y Html.BeginRouteForm proporcionan una funcionalidad similar.

  • Tiene una alternativa HTML Helper Html.BeginForm y Html.BeginRouteForm.

Sample:

<form asp-controller="Demo" asp-action="Register" method="post">
    <!-- Input and Submit elements -->
</form>

El asistente de etiquetas de formulario anterior genera el código HTML siguiente:

<form method="post" action="/Demo/Register">
    <!-- Input and Submit elements -->
    <input name="__RequestVerificationToken" type="hidden" value="<removed for brevity>">
</form>

El tiempo de ejecución MVC genera el valor de atributo action de los atributos del asistente de etiquetas Form asp-controller y asp-action. El asistente de etiquetas Form genera también un token de comprobación de solicitudes oculto que impide que se falsifiquen solicitudes entre sitios (cuando se usa con el atributo [ValidateAntiForgeryToken] en el método de acción HTTP Post). Proteger un formulario HTML puro de la falsificación de solicitudes entre sitios es difícil, pero el asistente de etiquetas de formulario proporciona este servicio automáticamente.

Uso de una ruta con nombre

El atributo del asistente de etiquetas asp-route puede generar también el marcado del atributo HTML action. Una aplicación con una ruta denominada register podría utilizar el siguiente marcado para la página de registro:

<form asp-route="register" method="post">
    <!-- Input and Submit elements -->
</form>

Muchas de las vistas de la carpeta Views/Account (generadas al crear una nueva aplicación web con cuentas individuales) contienen el atributo asp-route-returnurl :

<form asp-controller="Account" asp-action="Login"
     asp-route-returnurl="@ViewData["ReturnUrl"]"
     method="post" class="form-horizontal" role="form">

Nota

Con las plantillas integradas, returnUrl solo se rellena automáticamente cuando intentas acceder a un recurso que requiere autorización, pero no estás autenticado ni autorizado. Si se intenta realizar un acceso no autorizado, el middleware de seguridad redirige a la página de inicio de sesión con returnUrl configurado.

Asistente de etiquetas de acción de formulario

El asistente de etiquetas de acción de formulario genera el atributo formaction en la etiqueta <button ...> o <input type="image" ...> generadas. El atributo formaction controla el lugar donde un formulario envía sus datos. Se vincula a los elementos de <entrada> de tipo image y de < botón>. El asistente de etiquetas de acción de formulario permite el uso de varios atributos AnchorTagHelperasp- para controlar qué vínculo formaction se genera para el elemento correspondiente.

Atributos AnchorTagHelper admitidos para controlar el valor de formaction:

Atributo Descripción
asp-controller El nombre del controlador.
asp-action El nombre del método de acción.
asp-area El nombre del área.
asp-page El nombre de la página Razor.
asp-page-handler El nombre del controlador de la página Razor.
asp-route Nombre de la ruta.
asp-route-{value} Un valor único de ruta de URL. Por ejemplo: asp-route-id="1234".
asp-all-route-data Todos los valores de ruta.
asp-fragment El fragmento de dirección URL.

Ejemplo de envío al controlador

El marcado siguiente envía el formulario a la acción Index de HomeController cuando se selecciona el elemento de entrada o botón:

<form method="post">
    <button asp-controller="Home" asp-action="Index">Click Me</button>
    <input type="image" src="..." alt="Or Click Me" asp-controller="Home" 
                                asp-action="Index">
</form>

El marcado anterior genera el siguiente código HTML:

<form method="post">
    <button formaction="/Home">Click Me</button>
    <input type="image" src="..." alt="Or Click Me" formaction="/Home">
</form>

Enviar a la página ejemplo

El siguiente código envía el formulario a la página AboutRazor Page:

<form method="post">
    <button asp-page="About">Click Me</button>
    <input type="image" src="..." alt="Or Click Me" asp-page="About">
</form>

El marcado anterior genera el siguiente código HTML:

<form method="post">
    <button formaction="/About">Click Me</button>
    <input type="image" src="..." alt="Or Click Me" formaction="/About">
</form>

Ejemplo de envío a la ruta

Tenga en cuenta el punto de conexión /Home/Test:

public class HomeController : Controller
{
    [Route("/Home/Test", Name = "Custom")]
    public string Test()
    {
        return "This is the test page";
    }
}

El siguiente código envía el formulario al punto de conexión /Home/Test.

<form method="post">
    <button asp-route="Custom">Click Me</button>
    <input type="image" src="..." alt="Or Click Me" asp-route="Custom">
</form>

El marcado anterior genera el siguiente código HTML:

<form method="post">
    <button formaction="/Home/Test">Click Me</button>
    <input type="image" src="..." alt="Or Click Me" formaction="/Home/Test">
</form>

Asistente de etiquetas de entrada (Input)

El Asistente de etiquetas de entrada vincula un elemento de <entrada> HTML a una expresión de modelo en su vista de Razor.

Sintaxis:

<input asp-for="<Expression Name>">

El Input Tag Helper

  • Genera los atributos HTML id y name para el nombre de expresión especificado en el atributo asp-for. asp-for="Property1.Property2" equivale a m => m.Property1.Property2. El nombre de una expresión es lo que se usa para el valor de atributo asp-for. Para obtener más información, consulte la sección Nombres de expresión .

  • Establece el valor del atributo HTML type en función del tipo de modelo y los atributos de anotación de datos aplicados a la propiedad del modelo.

  • No sobrescribirá el valor del atributo HTML type cuando se especifica uno.

  • Genera atributos de validación HTML5 a partir de atributos de anotación de datos aplicados a las propiedades del modelo.

  • Tiene características del asistente de HTML que se superponen a Html.TextBoxFor y Html.EditorFor. Vea la sección Alternativas del asistente de HTML al asistente de etiquetas Input para obtener más información.

  • Permite establecer tipado fuerte. Si el nombre de la propiedad cambia y no actualiza el asistente de etiquetas, obtendrá un error similar al siguiente:

    An error occurred during the compilation of a resource required to process
    this request. Please review the following specific error details and modify
    your source code appropriately.
    
    Type expected
    'RegisterViewModel' does not contain a definition for 'Email' and no
    extension method 'Email' accepting a first argument of type 'RegisterViewModel'
    could be found (are you missing a using directive or an assembly reference?)
    

El asistente de etiquetas Input establece el atributo HTML type en función del tipo .NET. En la siguiente tabla se enumeran algunos tipos .NET habituales y el tipo HTML generado correspondiente (no incluimos aquí todos los tipos .NET).

Tipo de .NET Tipo de entrada
Bool type="checkbox"
Cuerda type="text"
Fecha y hora type="datetime-local"
Byte type="number"
Int type="number"
Sencillo, Doble type="number"

En la tabla siguiente se muestran algunos atributos de anotaciones de datos comunes que el asistente de etiquetas de entrada asignará a tipos de entrada específicos (no todos los atributos de validación se enumeran).

Atributo Tipo de entrada
[EmailAddress] type="email"
[Url] type="url"
[HiddenInput] tipo="oculto"
[Teléfono] type="tel"
[DataType(DataType.Password)] type="password"
[DataType(DataType.Date)] type="date"
[DataType(DataType.Time)] type="time"

Sample:

using System.ComponentModel.DataAnnotations;

namespace FormsTagHelper.ViewModels
{
    public class RegisterViewModel
    {
        [Required]
        [EmailAddress]
        [Display(Name = "Email Address")]
        public string Email { get; set; }

        [Required]
        [DataType(DataType.Password)]
        public string Password { get; set; }
    }
}
@model RegisterViewModel

<form asp-controller="Demo" asp-action="RegisterInput" method="post">
    <label>Email: <input asp-for="Email" /></label> <br />
    <label>Password: <input asp-for="Password" /></label><br />
    <button type="submit">Register</button>
</form>

El código anterior genera el siguiente código HTML:

<form method="post" action="/Demo/RegisterInput">
    Email:
    <input type="email" data-val="true"
            data-val-email="The Email Address field is not a valid email address."
            data-val-required="The Email Address field is required."
            id="Email" name="Email" value=""><br>
    Password:
    <input type="password" data-val="true"
            data-val-required="The Password field is required."
            id="Password" name="Password"><br>
    <button type="submit">Register</button>
    <input name="__RequestVerificationToken" type="hidden" value="<removed for brevity>">
</form>

Las anotaciones de datos que se aplican a las propiedades Email y Password generan metadatos en el modelo. El asistente de etiquetas de entrada usa esos metadatos del modelo y genera atributos HTML5 data-val-* (consulte Validación del modelo). Estos atributos describen los validadores que se van a adjuntar a los campos de entrada, lo que proporciona HTML5 discreto y validación de jQuery. Los atributos discretos tienen el formato data-val-rule="Error Message", donde la regla es el nombre de la regla de validación (por ejemplo data-val-required, , data-val-email, data-val-maxlength, etc.). Si se proporciona un mensaje de error en el atributo , se muestra como el valor del data-val-rule atributo . También hay atributos con el formato data-val-ruleName-argumentName="argumentValue" que aportan más información sobre la regla, por ejemplo, data-val-maxlength-max="1024".

Cuando se vinculan varios controles input a la misma propiedad, los controles generados comparten el mismo id, lo que hace que el marcado generado no sea válido. Para evitar duplicados, especifique explícitamente el atributo id para cada control.

Renderizado de entradas ocultas de casillas de verificación

Las casillas de verificación en HTML5 no envían un valor cuando están desmarcadas. Para permitir que se envíe un valor predeterminado para una casilla de verificación sin marcar, el asistente de etiquetas de entrada genera una entrada oculta adicional para las casillas de verificación.

Por ejemplo, considere el siguiente marcado Razor que utiliza el asistente de etiqueta de entrada para una propiedad de modelo booleano IsChecked:

<form method="post">
    <input asp-for="@Model.IsChecked" />
    <button type="submit">Submit</button>
</form>

La marca Razor anterior genera una marca HTML similar a la siguiente:

<form method="post">
    <input name="IsChecked" type="checkbox" value="true" />
    <button type="submit">Submit</button>

    <input name="IsChecked" type="hidden" value="false" /> 
</form>

El marcado HTML anterior muestra una entrada oculta adicional con un nombre de IsChecked y un valor de false. De manera predeterminada, esta entrada oculta se muestra al final del formulario. Cuando se envía el formulario:

  • Si la casilla IsChecked está marcada, tanto true como false son enviados como valores.
  • Si la casilla de verificación IsChecked no está marcada, solo se envía el valor oculto false.

El proceso de vinculación de modelos de ASP.NET Core solo lee el primer valor cuando se vincula a un valor bool, lo que da como resultado true para casillas de verificación marcadas y false para casillas de verificación sin marcar.

Para configurar el comportamiento de la representación de la entrada oculta, establezca la propiedad CheckBoxHiddenInputRenderMode en MvcViewOptions.HtmlHelperOptions. Por ejemplo:

services.Configure<MvcViewOptions>(options =>
    options.HtmlHelperOptions.CheckBoxHiddenInputRenderMode =
        CheckBoxHiddenInputRenderMode.None);

El código anterior desactiva la representación de la entrada oculta para las casillas de verificación estableciendo CheckBoxHiddenInputRenderMode en CheckBoxHiddenInputRenderMode.None. Para todos los modos de representación disponibles, vea el enum CheckBoxHiddenInputRenderMode.

Alternativas de HTML Helper al Input Tag Helper

Html.TextBox, Html.TextBoxFor, Html.Editory Html.EditorFor tienen características superpuestas con el asistente de etiquetas de entrada. El asistente de etiquetas Input establecerá automáticamente el atributo type, cosa que no ocurrirá con Html.TextBox ni Html.TextBoxFor. Html.Editor y Html.EditorFor controlan colecciones, objetos complejos y plantillas; el asistente de etiquetas de entrada no lo hace. El asistente de etiquetas de entrada, Html.EditorFor y Html.TextBoxFor están fuertemente tipados (usan expresiones lambda), mientras que Html.TextBox y Html.Editor no lo están (usan nombres de expresión).

Atributos de Html

@Html.Editor() y @Html.EditorFor() usan una entrada ViewDataDictionary especial denominada htmlAttributes al ejecutar sus plantillas predeterminadas. Si lo desea, este comportamiento se puede enriquecer con parámetros additionalViewData. La clave "htmlAttributes" no distingue entre mayúsculas y minúsculas. La clave "htmlAttributes" se controla de forma similar al objeto htmlAttributes pasado a asistentes de etiquetas Input como @Html.TextBox().

@Html.EditorFor(model => model.YourProperty, 
  new { htmlAttributes = new { @class="myCssClass", style="Width:100px" } })

Nombres de expresión

El valor del atributo asp-for es ModelExpression, que corresponde a la parte derecha de una expresión lambda. Por lo tanto, asp-for="Property1" se convierte m => m.Property1 en el código generado, por lo que no es necesario prefijo con Model. Puede usar el carácter "@" para iniciar una expresión insertada y moverla antes de m.:

@{
  var joe = "Joe";
}

<input asp-for="@joe">

Se genera lo siguiente:

<input type="text" id="joe" name="joe" value="Joe">

Con las propiedades de colección, asp-for="CollectionProperty[23].Member" genera el mismo nombre que asp-for="CollectionProperty[i].Member" si i tiene el valor 23.

Cuando ASP.NET Core MVC calcula el valor de ModelExpression, inspecciona varios orígenes, ModelState incluido. Fíjese en <input type="text" asp-for="Name">. El atributo value calculado es el primer valor distinto de null de:

  • La entrada ModelState con la clave "Name".
  • El resultado de la expresión Model.Name.

También se puede navegar a las propiedades secundarias a través de la ruta de acceso de propiedades del modelo de vista. Pensemos en una clase de modelo más compleja que contiene una propiedad secundaria Address.

public class AddressViewModel
{
    public string AddressLine1 { get; set; }
}
public class RegisterAddressViewModel
{
    public string Email { get; set; }

    [DataType(DataType.Password)]
    public string Password { get; set; }

    public AddressViewModel Address { get; set; }
}

En la vista, enlazamos a Address.AddressLine1:

@model RegisterAddressViewModel

<form asp-controller="Demo" asp-action="RegisterAddress" method="post">
    <label>Email: <input asp-for="Email" /></label> <br />
    <label>Password: <input asp-for="Password" /></label><br />
    <label>Address: <input asp-for="Address.AddressLine1" /></label><br />
    <button type="submit">Register</button>
</form>

Se genera el siguiente código HTML para Address.AddressLine1:

<input type="text" id="Address_AddressLine1" name="Address.AddressLine1" value="">

Colecciones y nombres de expresión

Como ejemplo, un modelo que contiene una matriz de Colors:

public class Person
{
    public List<string> Colors { get; set; }

    public int Age { get; set; }
}

El método de acción:

public IActionResult Edit(int id, int colorIndex)
{
    ViewData["Index"] = colorIndex;
    return View(GetPerson(id));
}

La siguiente Razor muestra cómo se accede a un elemento Color específico:

@model Person
@{
    var index = (int)ViewData["index"];
}

<form asp-controller="ToDo" asp-action="Edit" method="post">
    @Html.EditorFor(m => m.Colors[index])
    <label asp-for="Age"></label>
    <input asp-for="Age" /><br />
    <button type="submit">Post</button>
</form>

La plantilla Views/Shared/EditorTemplates/String.cshtml:

@model string

<label asp-for="@Model"></label>
<input asp-for="@Model" /> <br />

Ejemplo en el que se usa List<T>:

public class ToDoItem
{
    public string Name { get; set; }

    public bool IsDone { get; set; }
}

La siguiente Razor muestra cómo iterar sobre una colección:

@model List<ToDoItem>

<form asp-controller="ToDo" asp-action="Edit" method="post">
    <table>
        <tr> <th>Name</th> <th>Is Done</th> </tr>

        @for (int i = 0; i < Model.Count; i++)
        {
            <tr>
                @Html.EditorFor(model => model[i])
            </tr>
        }

    </table>
    <button type="submit">Save</button>
</form>

La plantilla Views/Shared/EditorTemplates/ToDoItem.cshtml:

@model ToDoItem

<td>
    <label asp-for="@Model.Name"></label>
    @Html.DisplayFor(model => model.Name)
</td>
<td>
    <input asp-for="@Model.IsDone" />
</td>

@*
    This template replaces the following Razor which evaluates the indexer three times.
    <td>
         <label asp-for="@Model[i].Name"></label>
         @Html.DisplayFor(model => model[i].Name)
     </td>
     <td>
         <input asp-for="@Model[i].IsDone" />
     </td>
*@

Si es posible, debe usarse foreach si el valor se va a utilizar en un contexto equivalente a asp-for o Html.DisplayFor. En general, for es mejor que foreach (si el escenario lo permite), ya que no necesita asignar ningún enumerador; sin embargo, la evaluación de un indizador en una expresión LINQ puede resultar caro y, por tanto, se debe minimizar.

 

Nota

El código de ejemplo comentado anterior muestra cómo reemplazaríamos la expresión lambda por el operador @ para tener acceso a cada elemento ToDoItem de la lista.

Asistente de etiquetas de área de texto (Textarea)

El asistente de etiquetas Textarea Tag Helper es similar al asistente de etiquetas de entrada.

  • Genera los atributos id y name, y los atributos de validación de datos del modelo para un elemento <textarea>.

  • Permite establecer tipado fuerte.

  • Alternativa del asistente de HTML: Html.TextAreaFor.

Sample:

using System.ComponentModel.DataAnnotations;

namespace FormsTagHelper.ViewModels
{
    public class DescriptionViewModel
    {
        [MinLength(5)]
        [MaxLength(1024)]
        public string Description { get; set; }
    }
}
@model DescriptionViewModel

<form asp-controller="Demo" asp-action="RegisterTextArea" method="post">
    <textarea asp-for="Description"></textarea>
    <button type="submit">Test</button>
</form>

Se genera el siguiente código HTML:

<form method="post" action="/Demo/RegisterTextArea">
  <textarea data-val="true"
   data-val-maxlength="The field Description must be a string or array type with a maximum length of &#x27;1024&#x27;."
   data-val-maxlength-max="1024"
   data-val-minlength="The field Description must be a string or array type with a minimum length of &#x27;5&#x27;."
   data-val-minlength-min="5"
   id="Description" name="Description">
  </textarea>
  <button type="submit">Test</button>
  <input name="__RequestVerificationToken" type="hidden" value="<removed for brevity>">
</form>

Asistente de etiquetas Label

  • Genera el título de la etiqueta y el atributo for en un elemento <etiqueta> para un nombre de expresión.

  • Alternativa del asistente de HTML: Html.LabelFor.

El Label Tag Helper proporciona las siguientes ventajas sobre un elemento label HTML puro:

  • Obtendrá automáticamente el valor de la etiqueta descriptiva del atributo Display. El nombre para mostrar que se busca puede cambiar con el tiempo y la combinación del atributo Display, y el asistente de etiquetas Label aplicará el elemento Display en cualquier lugar donde se use.

  • El código fuente contiene menos marcado.

  • Permite establecer tipado fuerte con la propiedad de modelo.

Sample:

using System.ComponentModel.DataAnnotations;

namespace FormsTagHelper.ViewModels
{
    public class SimpleViewModel
    {
        [Required]
        [EmailAddress]
        [Display(Name = "Email Address")]
        public string Email { get; set; }
    }
}

@model SimpleViewModel

<form asp-controller="Demo" asp-action="RegisterLabel" method="post">
    <label asp-for="Email"></label>
    <input asp-for="Email" /> <br />
</form>

Se genera el siguiente código HTML para el elemento <label>:

<label for="Email">Email Address</label>

El asistente de etiquetas Label genera el valor de atributo for de "Email", que es el identificador asociado al elemento <input>. Los asistentes de etiquetas generan elementos id y for coherentes para que se puedan asociar correctamente. El título de este ejemplo proviene del atributo Display. Si el modelo no contuviera un atributo Display, el título sería el nombre de propiedad de la expresión. Para invalidar el título predeterminado, añada un título dentro de la etiqueta label.

Asistentes de etiquetas de validación

Hay dos asistentes de etiquetas de validación. Validation Message Tag Helper (que muestra un mensaje de validación relativo a una única propiedad del modelo) y Validation Summary Tag Helper (que muestra un resumen de los errores de validación). Input Tag Helper agrega atributos de validación del lado cliente HTML5 a los elementos de entrada en función de los atributos de anotación de datos de las clases del modelo. La validación también se realiza en el lado servidor. El asistente de etiquetas de validación muestra estos mensajes de error cuando se produce un error de validación.

Asistente de etiquetas de mensaje de validación

  • Agrega el atributo HTML5 data-valmsg-for="property" al elemento span, que adjunta los mensajes de error de validación en el campo de entrada de la propiedad de modelo especificada. Cuando se produce un error de validación en el lado cliente, jQuery muestra el mensaje de error en el elemento <span>.

  • La validación también tiene lugar en el lado servidor. Es posible que los clientes tengan JavaScript deshabilitado y algunas validaciones solo se pueden realizar en el lado servidor.

  • Alternativa del asistente de HTML: Html.ValidationMessageFor.

El Validation Message Tag Helper se utiliza con el atributo asp-validation-for en un elemento HTML span.

<span asp-validation-for="Email"></span>

El asistente de etiquetas de mensaje de validación generará el siguiente código HTML:

<span class="field-validation-valid"
  data-valmsg-for="Email"
  data-valmsg-replace="true"></span>

Generalmente se utiliza el Validation Message Tag Helper después de un Input asistente de etiqueta para la misma propiedad. Gracias a esto, se mostrarán todos los mensajes de error de validación cerca de la entrada que produjo el error.

Nota

Debe tener una vista con las referencias de script de JavaScript y de jQuery adecuadas para la validación del lado cliente. Para obtener más información, consulte Validación de modelos.

Cuando se produce un error de validación del lado servidor (por ejemplo, porque haya una validación del lado servidor personalizada o porque la validación del lado cliente esté deshabilitada), MVC pone ese mensaje de error como cuerpo del elemento <span>.

<span class="field-validation-error" data-valmsg-for="Email"
            data-valmsg-replace="true">
   The Email Address field is required.
</span>

Asistente de etiquetas de resumen de validación

  • Tiene como destino <div> los elementos con el asp-validation-summary atributo .

  • Alternativa del asistente de HTML: @Html.ValidationSummary.

El Validation Summary Tag Helper se utiliza para mostrar un resumen de los mensajes de validación. El valor de atributo asp-validation-summary puede ser cualquiera de los siguientes:

asp-validation-summary (resumen de validación asp) Mensajes de validación que se muestran
All Nivel de propiedad y de modelo
ModelOnly Modelo
None Ninguno

Muestra

En el siguiente ejemplo, el modelo de datos tiene atributos DataAnnotation, lo que genera mensajes de error de validación sobre el elemento <input>. Cuando se produce un error de validación, el asistente de etiquetas de validación muestra el mensaje de error:

using System.ComponentModel.DataAnnotations;

namespace FormsTagHelper.ViewModels
{
    public class RegisterViewModel
    {
        [Required]
        [EmailAddress]
        [Display(Name = "Email Address")]
        public string Email { get; set; }

        [Required]
        [DataType(DataType.Password)]
        public string Password { get; set; }
    }
}
@model RegisterViewModel

<form asp-controller="Demo" asp-action="RegisterValidation" method="post">
    <div asp-validation-summary="ModelOnly"></div>
    <label>Email: <input asp-for="Email" /></label> <br />
    <span asp-validation-for="Email"></span><br />
    <label>Password: <input asp-for="Password" /></label><br />
    <span asp-validation-for="Password"></span><br />
    <button type="submit">Register</button>
</form>

El código HTML generado (cuando el modelo es válido) es este:

<form action="/DemoReg/Register" method="post">
  <label>Email: <input name="Email" id="Email" type="email" value=""
   data-val-required="The Email field is required."
   data-val-email="The Email field is not a valid email address."
   data-val="true"></label><br>
  <span class="field-validation-valid" data-valmsg-replace="true"
   data-valmsg-for="Email"></span><br>
  <label>Password: <input name="Password" id="Password" type="password"
   data-val-required="The Password field is required." data-val="true"></label><br>
  <span class="field-validation-valid" data-valmsg-replace="true"
   data-valmsg-for="Password"></span><br>
  <button type="submit">Register</button>
  <input name="__RequestVerificationToken" type="hidden" value="<removed for brevity>">
</form>

Asistente de etiquetas de selección - Select Tag Helper

  • Genera los elementos select y option asociados para las propiedades de su modelo.

  • Tiene una alternativa HTML Helper Html.DropDownListFor y Html.ListBoxFor.

Select Tag Helper asp-for especifica el nombre de la propiedad de modelo del elemento select, mientras que asp-items especifica los elementos option. Por ejemplo:

<select asp-for="Country" asp-items="Model.Countries"></select> 

Sample:

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

namespace FormsTagHelper.ViewModels
{
    public class CountryViewModel
    {
        public string Country { get; set; }

        public List<SelectListItem> Countries { get; } = new List<SelectListItem>
        {
            new SelectListItem { Value = "MX", Text = "Mexico" },
            new SelectListItem { Value = "CA", Text = "Canada" },
            new SelectListItem { Value = "US", Text = "USA" },
        };
    }
}

El Index método inicializa el CountryViewModel, establece el país seleccionado y lo pasa a la Index vista.

public IActionResult Index()
{
    var model = new CountryViewModel();
    model.Country = "CA";
    return View(model);
}

El método HTTP POST Index muestra la selección:

[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Index(CountryViewModel model)
{
    if (ModelState.IsValid)
    {
        var msg = model.Country + " selected";
        return RedirectToAction("IndexSuccess", new { message = msg });
    }

    // If we got this far, something failed; redisplay form.
    return View(model);
}

La vista Index:

@model CountryViewModel

<form asp-controller="Home" asp-action="Index" method="post">
    <select asp-for="Country" asp-items="Model.Countries"></select> 
    <br /><button type="submit">Register</button>
</form>

Genera el siguiente código HTML (con la selección "CA"):

<form method="post" action="/">
     <select id="Country" name="Country">
       <option value="MX">Mexico</option>
       <option selected="selected" value="CA">Canada</option>
       <option value="US">USA</option>
     </select>
       <br /><button type="submit">Register</button>
     <input name="__RequestVerificationToken" type="hidden" value="<removed for brevity>">
   </form>

Nota

No se recomienda usar ViewBag o ViewData con el asistente de etiquetas Select. Un modelo de vista es más sólido al proporcionar metadatos de MVC y menos problemáticos.

El valor del asp-for atributo es un caso especial y no requiere un Model prefijo. Los demás atributos del asistente de etiquetas hacen lo mismo (como asp-items).

<select asp-for="Country" asp-items="Model.Countries"></select> 

Enlace con enum

A menudo, conviene usar <select> con una propiedad enum y generar los elementos SelectListItem a partir de valores enum.

Sample:

public class CountryEnumViewModel
{
    public CountryEnum EnumCountry { get; set; }
}
using System.ComponentModel.DataAnnotations;

namespace FormsTagHelper.ViewModels
{
    public enum CountryEnum
    {
        [Display(Name = "United Mexican States")]
        Mexico,
        [Display(Name = "United States of America")]
        USA,
        Canada,
        France,
        Germany,
        Spain
    }
}

El método GetEnumSelectList genera un objeto SelectList para una enumeración.

@model CountryEnumViewModel

<form asp-controller="Home" asp-action="IndexEnum" method="post">
    <select asp-for="EnumCountry" 
            asp-items="Html.GetEnumSelectList<CountryEnum>()">
    </select> 
    <br /><button type="submit">Register</button>
</form>

Puede marcar la lista de enumeradores con el atributo Display para obtener una interfaz de usuario más completa:

using System.ComponentModel.DataAnnotations;

namespace FormsTagHelper.ViewModels
{
    public enum CountryEnum
    {
        [Display(Name = "United Mexican States")]
        Mexico,
        [Display(Name = "United States of America")]
        USA,
        Canada,
        France,
        Germany,
        Spain
    }
}

Se genera el siguiente código HTML:

<form method="post" action="/Home/IndexEnum">
    <select data-val="true" data-val-required="The EnumCountry field is required."
            id="EnumCountry" name="EnumCountry">
        <option value="0">United Mexican States</option>
        <option value="1">United States of America</option>
        <option value="2">Canada</option>
        <option value="3">France</option>
        <option value="4">Germany</option>
        <option selected="selected" value="5">Spain</option>
    </select>
    <br /><button type="submit">Register</button>
    <input name="__RequestVerificationToken" type="hidden" value="<removed for brevity>">
</form>

Agrupamiento de opciones

El elemento HTML <optgroup> se genera cuando el modelo de vista contiene uno o más objetos SelectListGroup.

CountryViewModelGroup agrupa los elementos SelectListItem en los grupos "North America" y "Europe":

public class CountryViewModelGroup
{
    public CountryViewModelGroup()
    {
        var NorthAmericaGroup = new SelectListGroup { Name = "North America" };
        var EuropeGroup = new SelectListGroup { Name = "Europe" };

        Countries = new List<SelectListItem>
        {
            new SelectListItem
            {
                Value = "MEX",
                Text = "Mexico",
                Group = NorthAmericaGroup
            },
            new SelectListItem
            {
                Value = "CAN",
                Text = "Canada",
                Group = NorthAmericaGroup
            },
            new SelectListItem
            {
                Value = "US",
                Text = "USA",
                Group = NorthAmericaGroup
            },
            new SelectListItem
            {
                Value = "FR",
                Text = "France",
                Group = EuropeGroup
            },
            new SelectListItem
            {
                Value = "ES",
                Text = "Spain",
                Group = EuropeGroup
            },
            new SelectListItem
            {
                Value = "DE",
                Text = "Germany",
                Group = EuropeGroup
            }
      };
    }

    public string Country { get; set; }

    public List<SelectListItem> Countries { get; }

Los dos grupos se muestran de la siguiente manera:

Ejemplo de agrupamiento de opciones

El código HTML generado:

 <form method="post" action="/Home/IndexGroup">
      <select id="Country" name="Country">
          <optgroup label="North America">
              <option value="MEX">Mexico</option>
              <option value="CAN">Canada</option>
              <option value="US">USA</option>
          </optgroup>
          <optgroup label="Europe">
              <option value="FR">France</option>
              <option value="ES">Spain</option>
              <option value="DE">Germany</option>
          </optgroup>
      </select>
      <br /><button type="submit">Register</button>
      <input name="__RequestVerificationToken" type="hidden" value="<removed for brevity>">
 </form>

Selección múltiple

El asistente de etiquetas selectas generará automáticamente el atributo multiple = "multiple" si la propiedad especificada en el atributo asp-for es un IEnumerable. Por ejemplo, si tenemos el siguiente modelo:

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

namespace FormsTagHelper.ViewModels
{
    public class CountryViewModelIEnumerable
    {
        public IEnumerable<string> CountryCodes { get; set; }

        public List<SelectListItem> Countries { get; } = new List<SelectListItem>
        {
            new SelectListItem { Value = "MX", Text = "Mexico" },
            new SelectListItem { Value = "CA", Text = "Canada" },
            new SelectListItem { Value = "US", Text = "USA"    },
            new SelectListItem { Value = "FR", Text = "France" },
            new SelectListItem { Value = "ES", Text = "Spain"  },
            new SelectListItem { Value = "DE", Text = "Germany"}
         };
    }
}

Con la siguiente vista:

@model CountryViewModelIEnumerable

<form asp-controller="Home" asp-action="IndexMultiSelect" method="post">
    <select asp-for="CountryCodes" asp-items="Model.Countries"></select> 
    <br /><button type="submit">Register</button>
</form>

Se genera el siguiente código HTML:

<form method="post" action="/Home/IndexMultiSelect">
    <select id="CountryCodes"
    multiple="multiple"
    name="CountryCodes"><option value="MX">Mexico</option>
<option value="CA">Canada</option>
<option value="US">USA</option>
<option value="FR">France</option>
<option value="ES">Spain</option>
<option value="DE">Germany</option>
</select>
    <br /><button type="submit">Register</button>
  <input name="__RequestVerificationToken" type="hidden" value="<removed for brevity>">
</form>

Sin selección

Si ve que usa la opción "sin especificar" en varias páginas, puede crear una plantilla para no tener que repetir el código HTML:

@model CountryViewModel

<form asp-controller="Home" asp-action="IndexEmpty" method="post">
    @Html.EditorForModel()
    <br /><button type="submit">Register</button>
</form>

La plantilla Views/Shared/EditorTemplates/CountryViewModel.cshtml:

@model CountryViewModel

<select asp-for="Country" asp-items="Model.Countries">
    <option value="">--none--</option>
</select>

La adición de elementos HTML <option> no se limita al caso Sin selección. Por ejemplo, el siguiente método de vista y acción generará HTML similar al código anterior:

public IActionResult IndexNone()
{
    var model = new CountryViewModel();
    model.Countries.Insert(0, new SelectListItem("<none>", ""));
    return View(model);
}
@model CountryViewModel

<form asp-controller="Home" asp-action="IndexEmpty" method="post">
    <select asp-for="Country">
        <option value="">&lt;none&gt;</option>
        <option value="MX">Mexico</option>
        <option value="CA">Canada</option>
        <option value="US">USA</option>
    </select> 
    <br /><button type="submit">Register</button>
</form>

El elemento correcto <option> se seleccionará (contiene el selected="selected" atributo) en función del valor actual Country .

public IActionResult IndexOption(int id)
{
    var model = new CountryViewModel();
    model.Country = "CA";
    return View(model);
}
 <form method="post" action="/Home/IndexEmpty">
      <select id="Country" name="Country">
          <option value="">&lt;none&gt;</option>
          <option value="MX">Mexico</option>
          <option value="CA" selected="selected">Canada</option>
          <option value="US">USA</option>
      </select>
      <br /><button type="submit">Register</button>
   <input name="__RequestVerificationToken" type="hidden" value="<removed for brevity>">
 </form>

Recursos adicionales