Comparteix a través de


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 de HTML proporcionan un método alternativo para un asistente de etiquetas específico, pero es importante tener en cuenta que los asistentes de etiquetas no reemplazan a los asistentes de HTML y que no hay un asistente de etiquetas para cada asistente de HTML. Si existe una alternativa del asistente de HTML, se mencionará aquí.

Asistente de etiquetas de formulario (Form)

El asistente de etiquetas Form hace lo siguiente:

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

  • Genera un token 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).

  • 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 Html.BeginForm y Html.BeginRouteForm como alternativa del asistente de HTML.

Sample:

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

El asistente de etiquetas Form anterior genera el siguiente HTML:

<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 elemento HTML Form puro de la falsificación de solicitudes entre sitios no es tarea fácil, y el asistente de etiquetas Form presta este servicio.

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 (que se genera cuando se crea una aplicación web con Cuentas de usuario individuales) contienen el atributoasp-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 se rellena automáticamente solo cuando alguien intenta obtener acceso a un recurso autorizado, pero no se ha autenticado o no tiene autorización. 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 <input> de tipo image y <button>. El asistente de etiquetas de acción de formulario permite el uso de varios atributos AnchorTagHelper asp- 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 dirección 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 input o button:

<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>

Ejemplo de envío a la página

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 marcado siguiente 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 etiqueta de entrada vincula un elemento HTML de <entrada> a una expresión de modelo en tu vista razor.

Sintaxis:

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

El asistente de etiquetas Input hace lo siguiente:

  • Genera los atributos HTML y del nombre de expresión especificado en el atributo. 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. Vea la sección Nombres de expresión para obtener más información.

  • Establece el valor del atributo HTML type basándose en el tipo de modelo y los atributos data annotation aplicados a la propiedad del modelo

  • No sobrescribirá el valor de atributo HTML type cuando se especifique uno.

  • Genera atributos de validación HTML5 a partir de atributos data annotation 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 se actualiza el asistente de etiquetas, aparecerá 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"
String type="text"
DateTime type="datetime-local"
Byte type="number"
Int type="number"
Single, Double type="number"

En la siguiente tabla se muestran algunos atributos de anotación de datos comunes que el asistente de etiquetas Input asignará a tipos de entrada concretos (no incluimos aquí todos los atributo de validación):

Atributo Tipo de entrada
[EmailAddress] type="email"
[Url] type="url"
[HiddenInput] type="hidden"
[Phone] 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 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 rule es el nombre de la regla de validación (como data-val-required, data-val-email, data-val-maxlength, etc.) Si se proporciona un mensaje de error en el atributo, se muestra como valor para el atributo data-val-rule. 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 del asistente de HTML al asistente de etiquetas Input

Html.TextBox, Html.TextBoxFor, Html.Editor y Html.EditorFor tienen características que se superponen al asistente de etiquetas Input. 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, pero el asistente de etiquetas Input no. El Input Tag Helper, Html.EditorFor y Html.TextBoxFor están fuertemente tipados (utilizan expresiones lambda); Html.TextBox y Html.Editor no lo están (utilizan nombres de expresiones).

HtmlAttributes

@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. En la clave "htmlAttributes" se 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 una ModelExpression y la parte de la derecha de una expresión lambda. Por tanto, asp-for="Property1" se convierte en m => m.Property1 en el código generado, motivo por el que no es necesario incluir el prefijo 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 el siguiente HTML:

<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. Puede que JavaScript esté deshabilitado en los clientes, mientras que hay algunas validaciones que 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 más información, vea Introduction to model validation in ASP.NET Core MVC (Introducción a la validación de modelos en ASP.NET Core MVC).

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 todos los elementos <div> con el atributo asp-validation-summary.

  • 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 Mensajes de validación que se muestran
All Nivel de modelo y de propiedad
ModelOnly Modelo
None None

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)

  • Genera el elemento select y el elemento asociado option de las propiedades del modelo.

  • Tiene Html.DropDownListFor y Html.ListBoxFor como alternativa del asistente de HTML.

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 método Index inicializa CountryViewModel, establece el país seleccionado y lo pasa a la vista Index.

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 eficaz a la hora de proporcionar metadatos MVC y suele ser menos problemático.

El valor de atributo asp-for es un caso especial y no necesita un prefijo Model, mientras que los otros atributos del asistente de etiquetas sí (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; }

Aquí mostramos los dos grupos:

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 método de acción y vista siguientes generarán un código 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>

Se seleccionará el elemento <option> correcto (que contenga el atributo selected="selected") en función del valor real de 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