Auxiliares de marca em formulários no ASP.NET Core

De Rick Anderson, N. Taylor Mullen, Dave Paquette e Jerrie Pelser

Este documento demonstra como é o trabalho com Formulários e os elementos HTML usados comumente em um Formulário. O elemento HTML Formulário fornece o mecanismo primário que os aplicativos Web usam para postar dados para o servidor. A maior parte deste documento descreve os Auxiliares de marca e como eles podem ajudar você a criar formulários HTML robustos de forma produtiva. É recomendável que você leia Introdução ao auxiliares de marca antes de ler este documento.

Em muitos casos, os Auxiliares HTML fornecem uma abordagem alternativa a um Auxiliar de Marca específico, mas é importante reconhecer que os Auxiliares de Marca não substituem os Auxiliares HTML e que não há um Auxiliar de Marca para cada Auxiliar HTML. Quando existe um Auxiliar HTML alternativo, ele é mencionado.

O Auxiliar de marca de formulário

O Auxiliar de Marca de Formulário:

  • Gera o valor do atributo HTML <FORM>action para uma ação do controlador MVC ou uma rota nomeada

  • Gera um Token de verificação de solicitação oculto para evitar a falsificação de solicitações entre sites (quando usado com o atributo [ValidateAntiForgeryToken] no método de ação HTTP Post)

  • Fornece o atributo asp-route-<Parameter Name>, em que <Parameter Name> é adicionado aos valores de rota. Os parâmetros routeValues para Html.BeginForm e Html.BeginRouteForm fornecem funcionalidade semelhante.

  • Tem uma alternativa de Auxiliar HTML Html.BeginForm e Html.BeginRouteForm

Exemplo:

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

O Auxiliar de marca de formulário acima gera o HTML a seguir:

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

O runtime do MVC gera o valor do atributo action dos atributos asp-controller e asp-action do Auxiliar de marca de formulário. O Auxiliar de marca de formulário também gera um Token de verificação de solicitação oculto para evitar a falsificação de solicitações entre sites (quando usado com o atributo [ValidateAntiForgeryToken] no método de ação HTTP Post). É difícil proteger um Formulário HTML puro contra falsificação de solicitações entre sites e o Auxiliar de marca de formulário fornece este serviço para você.

Usando uma rota nomeada

O atributo do Auxiliar de Marca asp-route também pode gerar a marcação para o atributo HTML action. Um aplicativo com uma rota chamada register poderia usar a seguinte marcação para a página de registro:

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

Muitas das exibições na pasta Modos de Exibição/Conta (gerada quando você cria um novo aplicativo Web com Contas de usuário individuais) contêm o atributo asp-route-returnurl:

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

Observação

Com os modelos internos, returnUrl só é preenchido automaticamente quando você tenta acessar um recurso autorizado, mas não está autenticado ou autorizado. Quando você tenta fazer um acesso não autorizado, o middleware de segurança o redireciona para a página de logon com o returnUrl definido.

Auxiliar de Marcação de Ação de Formulário

O Auxiliar de Marcação de Ação de Formulário gera o atributo formaction na marcação <button ...> ou <input type="image" ...> gerada. O atributo formaction controla onde um formulário envia seus dados. Ele se associa à elementos de <entrada> do tipo image e elementos de <botão>. O Auxiliar de Marcação de Ação de Formulário permite o uso de vários atributos AnchorTagHelperasp- para controlar qual link formaction será gerado para o elemento correspondente.

Atributos AnchorTagHelper com suporte para controlar o valor de formaction:

Atributo Descrição
asp-controller O nome do controlador.
asp-action O nome do método da ação.
asp-area O nome da área.
asp-page O nome da página Razor.
asp-page-handler O nome do manipulador da página doRazor.
asp-route O nome da rota.
asp-route-{value} Um único valor de rota de URL. Por exemplo, asp-route-id="1234".
asp-all-route-data Todos os valores de rota.
asp-fragment O fragmento de URL.

Exemplo de Enviar ao controlador

A marcação a seguir envia o formulário à ação Index de HomeController quando a entrada ou botão são escolhidos:

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

A marcação anterior gera o seguinte HTML:

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

Exemplo de Enviar a uma página

A marcação a seguir envia o formulário para a Página AboutRazor:

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

A marcação anterior gera o seguinte HTML:

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

Exemplo de Enviar a uma rota

Considere o ponto de extremidade /Home/Test:

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

A marcação a seguir envia o formulário ao ponto de extremidade /Home/Test.

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

A marcação anterior gera o seguinte HTML:

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

O auxiliar de marca de entrada

O Auxiliar de marca de entrada associa um elemento de<entrada> do HTML a uma expressão de modelo em seu modo de exibição do Razor.

Sintaxe:

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

O auxiliar de marca de entrada:

  • Gera os atributos HTML id e name para o nome da expressão especificada no atributo asp-for. asp-for="Property1.Property2" é equivalente a m => m.Property1.Property2. O nome da expressão é o que é usado para o valor do atributo asp-for. Consulte a seção Nomes de expressão para obter informações adicionais.

  • Define o valor do atributo HTML type com base no de tipo de modelo e em atributos de anotação de dados aplicados à propriedade de modelo

  • O valor do atributo HTML type não será substituído quando um for especificado

  • Gera atributos de validação HTML5 de atributos de anotação de dados aplicados a propriedades de modelo

  • Tem uma sobreposição de recursos de Auxiliar HTML com Html.TextBoxFor e Html.EditorFor. Consulte a seção Alternativas de Auxiliar HTML ao Auxiliar de marca de entrada para obter detalhes.

  • Fornece tipagem forte. Se o nome da propriedade for alterado e você não atualizar o Auxiliar de marca, você verá um erro semelhante ao seguinte:

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

O Auxiliar de marca Input define o atributo HTML type com base no tipo .NET. A tabela a seguir lista alguns tipos .NET comuns e o tipo HTML gerado (não estão listados todos os tipos .NET).

Tipo .NET Tipo de entrada
Bool type="checkbox"
String type="text"
Datetime type="datetime-local"
Byte type="number"
int type="number"
Single e Double type="number"

A tabela a seguir mostra alguns atributos de anotações de dados comuns que o auxiliar de marca de entrada mapeará para tipos de entrada específicos (não são listados todos os atributos de validação):

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"

Exemplo:

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">
    Email:  <input asp-for="Email" /> <br />
    Password: <input asp-for="Password" /><br />
    <button type="submit">Register</button>
</form>

O código acima gera o seguinte 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>

As anotações de dados aplicadas às propriedades Email e Password geram metadados no modelo. O Auxiliar de marca de entrada consome os metadados do modelo e produz atributos HTML5data-val-* (consulte Validação de modelo). Esses atributos descrevem os validadores a serem anexados aos campos de entrada. Isso fornece validação de jQuery e HTML5 discreto. Os atributos não discretos têm o formato data-val-rule="Error Message", em que regra é o nome da regra de validação (como data-val-required, data-val-email, data-val-maxlengthetc.) Se uma mensagem de erro for fornecida no atributo , ela será exibida como o valor do atributo data-val-rule. Também há atributos do formulário data-val-ruleName-argumentName="argumentValue" que fornecem detalhes adicionais sobre a regra, por exemplo, data-val-maxlength-max="1024".

Ao associar vários controles do input à mesma propriedade, os controles gerados compartilham o mesmo id, o que torna a marcação gerada inválida. Para evitar duplicatas, especifique o atributo id para cada controle explicitamente.

Renderização de entrada oculta da caixa de seleção

As caixas de seleção em HTML5 não enviam um valor quando estão desmarcadas. Para habilitar um valor padrão a ser enviado para uma caixa de seleção desmarcada, o Auxiliar de Marca de Entrada gera uma entrada oculta adicional para caixas de seleção.

Por exemplo, considere a marcação Razor a seguir que usa o Auxiliar de Marca de Entrada para uma propriedade de modelo booliano IsChecked:

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

A marcação Razor anterior gera marcação HTML semelhante à seguinte:

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

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

A marcação HTML anterior mostra uma entrada oculta adicional com um nome de IsChecked e um valor de false. Por padrão, essa entrada oculta é renderizada no final do formulário. Quando o formulário é enviado:

  • Se a entrada da IsChecked caixa de seleção for marcada, true e false serão enviadas como valores.
  • Se a entrada da caixa de seleção IsChecked estiver desmarcada, somente o valor false de entrada oculto será enviado.

O processo de associação de modelo do ASP.NET Core lê apenas o primeiro valor ao associar a um valor bool, o que resulta em true para caixas de seleção marcadas e false para caixas de seleção desmarcadas.

Para configurar o comportamento da renderização de entrada oculta, defina a propriedade CheckBoxHiddenInputRenderMode em MvcViewOptions.HtmlHelperOptions. Por exemplo:

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

O código anterior desabilita a renderização de entrada oculta para caixas de seleção definindo CheckBoxHiddenInputRenderMode como CheckBoxHiddenInputRenderMode.None. Para todos os modos de renderização disponíveis, consulte a enumeração CheckBoxHiddenInputRenderMode.

Alternativas de Auxiliar HTML ao Auxiliar de marca de entrada

Html.TextBox, Html.TextBoxFor, Html.Editor e Html.EditorFor têm recursos que se sobrepõem aos di Auxiliar de marca de entrada. O Auxiliar de marca de entrada define automaticamente o atributo type; Html.TextBox e Html.TextBoxFor não o fazem. Html.Editor e Html.EditorFor manipulam coleções, objetos complexos e modelos; o Auxiliar de marca de entrada não o faz. O Auxiliar de marca de entrada Html.EditorFor e Html.TextBoxFor são fortemente tipados (eles usam expressões lambda); Html.TextBox e Html.Editor não usam (eles usam nomes de expressão).

HtmlAttributes

@Html.Editor() e @Html.EditorFor() usam uma entrada ViewDataDictionary especial chamada htmlAttributes ao executar seus modelos padrão. Esse comportamento pode ser aumentado usando parâmetros additionalViewData. A chave "htmlAttributes" diferencia maiúsculas de minúsculas. A chave "htmlAttributes" é tratada de forma semelhante ao objeto htmlAttributes passado para auxiliares de entrada como @Html.TextBox().

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

Nomes de expressão

O valor do atributo asp-for é um ModelExpression e o lado direito de uma expressão lambda. Portanto, asp-for="Property1" se torna m => m.Property1 no código gerado e é por isso você não precisa colocar o prefixo Model. Você pode usar o caractere "@" para iniciar uma expressão embutida e mover para antes de m.:

@{
  var joe = "Joe";
}

<input asp-for="@joe">

Gera o seguinte:

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

Com propriedades de coleção, asp-for="CollectionProperty[23].Member" gera o mesmo nome que asp-for="CollectionProperty[i].Member" quando i tem o valor 23.

Quando o ASP.NET Core MVC calcula o valor de ModelExpression, ele inspeciona várias fontes, inclusive o ModelState. Considere o <input type="text" asp-for="Name">. O atributo value calculado é o primeiro valor não nulo:

  • Da entrada de ModelState com a chave "Name".
  • Do resultado da expressão Model.Name.

Você também pode navegar para propriedades filho usando o caminho da propriedade do modelo de exibição. Considere uma classe de modelo mais complexa que contém uma propriedade Address filho.

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

Na exibição, associamos a Address.AddressLine1:

@model RegisterAddressViewModel

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

O HTML a seguir é gerado para Address.AddressLine1:

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

Nomes de expressão e coleções

Exemplo, um modelo que contém uma matriz de Colors:

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

    public int Age { get; set; }
}

O método de ação:

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

O Razor a seguir mostra como você acessa um 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>

O modelo Views/Shared/EditorTemplates/String.cshtml:

@model string

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

Exemplo usando List<T>:

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

    public bool IsDone { get; set; }
}

O Razor a seguir mostra como iterar em uma coleção:

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

O modelo 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>
*@

foreach deve ser usado, se possível, quando o valor está prestes a ser usado em um contexto equivalente asp-for ou Html.DisplayFor. Em geral, for é melhor do que foreach (se o cenário permitir) porque não é necessário alocar um enumerador; no entanto, avaliar um indexador em uma expressão LINQ pode ser caro, o que deve ser minimizado.

 

Observação

O código de exemplo comentado acima mostra como você substituiria a expressão lambda pelo operador @ para acessar cada ToDoItem na lista.

Auxiliar de marca de área de texto

O auxiliar de marca Textarea Tag Helper é semelhante ao Auxiliar de marca de entrada.

  • Gera os atributos id e name, bem como os atributos de validação de dados do modelo para um elemento <textarea>.

  • Fornece tipagem forte.

  • Alternativa de Auxiliar HTML: Html.TextAreaFor

Exemplo:

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>

O HTML a seguir é gerado:

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

O auxiliar de marca de rótulo

  • Gera a legenda do rótulo e o atributo for em um elemento <rótulo> para um nome de expressão

  • Alternativa de Auxiliar HTML: Html.LabelFor.

O Label Tag Helper fornece os seguintes benefícios em comparação com um elemento de rótulo HTML puro:

  • Você obtém automaticamente o valor do rótulo descritivo do atributo Display. O nome de exibição desejado pode mudar com o tempo e a combinação do atributo Display e do Auxiliar de Marca de Rótulo aplicará Display em qualquer lugar em que for usado.

  • Menos marcação no código-fonte

  • Tipagem forte com a propriedade de modelo.

Exemplo:

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>

O HTML a seguir é gerado para o elemento <label>:

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

O Auxiliar de marca de rótulo gerou o valor do atributo for de "Email", que é a ID associada ao elemento <input>. Auxiliares de marca geram elementos id e for consistentes para que eles possam ser associados corretamente. A legenda neste exemplo é proveniente do atributo Display. Se o modelo não contivesse um atributo Display, a legenda seria o nome da propriedade da expressão. Para substituir a legenda padrão, adicione um legenda dentro da marca de rótulo.

Os auxiliares de marca de validação

Há dois auxiliares de marca de validação. O Validation Message Tag Helper (que exibe uma mensagem de validação para uma única propriedade em seu modelo) e o Validation Summary Tag Helper (que exibe um resumo dos erros de validação). O Input Tag Helper adiciona atributos de validação do lado do cliente HTML5 para elementos de entrada baseados em atributos de anotação de dados em suas classes de modelo. A validação também é executada no servidor. O Auxiliar de marca de validação exibe essas mensagens de erro quando ocorre um erro de validação.

O Auxiliar de marca de mensagem de validação

  • Adiciona o atributo data-valmsg-for="property"HTML5 ao elemento span, que anexa as mensagens de erro de validação no campo de entrada da propriedade do modelo especificado. Quando ocorre um erro de validação do lado do cliente, jQuery exibe a mensagem de erro no elemento <span>.

  • A validação também é feita no servidor. Os clientes poderão ter o JavaScript desabilitado e parte da validação só pode ser feita no lado do servidor.

  • Alternativa de Auxiliar HTML: Html.ValidationMessageFor

O Validation Message Tag Helper é usado com o atributo asp-validation-for em um elemento HTML span.

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

O Auxiliar de marca de mensagem de validação gerará o HTML a seguir:

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

Geralmente, você usa o Validation Message Tag Helper após um Auxiliar de Marca Input para a mesma propriedade. Fazer isso exibe as mensagens de erro de validação próximo à entrada que causou o erro.

Observação

É necessário ter uma exibição com as referências de script jQuery e JavaScript corretas em vigor para a validação do lado do cliente. Consulte Validação de Modelo para obter mais informações.

Quando ocorre um erro de validação do lado do servidor (por exemplo, quando você tem validação do lado do servidor personalizada ou a validação do lado do cliente está desabilitada), o MVC coloca essa mensagem de erro como o corpo do elemento <span>.

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

Auxiliar de marca de resumo de validação

  • Tem como alvo elementos <div> com o atributo asp-validation-summary

  • Alternativa de Auxiliar HTML: @Html.ValidationSummary

O Validation Summary Tag Helper é usado para exibir um resumo das mensagens de validação. O valor do atributo asp-validation-summary pode ser qualquer um dos seguintes:

asp-validation-summary Mensagens de validação exibidas
All Nível da propriedade e do modelo
ModelOnly Modelar
None Nenhum

Amostra

No exemplo a seguir, o modelo de dados é decorado com atributos DataAnnotation, o que gera mensagens de erro de validação no elemento <input>. Quando ocorre um erro de validação, o Auxiliar de marca de validação exibe a mensagem de erro:

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>
    Email:  <input asp-for="Email" /> <br />
    <span asp-validation-for="Email"></span><br />
    Password: <input asp-for="Password" /><br />
    <span asp-validation-for="Password"></span><br />
    <button type="submit">Register</button>
</form>

O código HTML gerado (quando o modelo é válido):

<form action="/DemoReg/Register" method="post">
  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"><br>
  <span class="field-validation-valid" data-valmsg-replace="true"
   data-valmsg-for="Email"></span><br>
  Password: <input name="Password" id="Password" type="password"
   data-val-required="The Password field is required." data-val="true"><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>

Auxiliar de Marca de Seleção

  • Gera select e os elementos option associados para as propriedades do modelo.

  • Tem uma alternativa de Auxiliar HTML Html.DropDownListFor e Html.ListBoxFor

O Select Tag Helperasp-for especifica o nome da propriedade do modelo para o elemento select e asp-items especifica os elementos option. Por exemplo:

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

Exemplo:

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"  },
        };
    }
}

O método Index inicializa o CountryViewModel, define o país selecionado e o transmite para a exibição Index.

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

O método Index HTTP POST exibe a seleção:

[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);
}

A exibição 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>

Que gera o seguinte HTML (com "CA" selecionado):

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

Observação

Não é recomendável usar ViewBag ou ViewData com o Auxiliar de Marca de Seleção. Um modelo de exibição é mais robusto para fornecer metadados MVC e, geralmente, menos problemático.

O valor do atributo asp-for é um caso especial e não requer um prefixo Model, os outros atributos do Auxiliar de marca requerem (como asp-items)

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

Associação de enumeração

Geralmente, é conveniente usar <select> com uma propriedade enum e gerar os elementos SelectListItem dos valores enum.

Exemplo:

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

O método GetEnumSelectList gera um objeto SelectList para uma enumeração.

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

É possível decorar sua lista de enumeradores com o atributo Display para obter uma interface do usuário mais rica:

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

O HTML a seguir é gerado:

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

Grupo de opções

O elemento HTML <optgroup> é gerado quando o modelo de exibição contém um ou mais objetos SelectListGroup.

O CountryViewModelGroup agrupa os elementos SelectListItem nos grupos "América do Norte" e "Europa":

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

Os dois grupos são mostrados abaixo:

option group example

O HTML gerado:

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

Seleção múltipla

O Auxiliar de Marca de Seleção gerará automaticamente o atributo multiple = "multiple" se a propriedade especificada no atributo asp-for for um IEnumerable. Por exemplo, considerando o seguinte 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"}
         };
    }
}

Com a seguinte exibição:

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

Gera o seguinte 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>

Nenhuma seleção

Se acabar usando a opção "não especificado" em várias páginas, você poderá criar um modelo para eliminar o HTML de repetição:

@model CountryViewModel

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

O modelo Views/Shared/EditorTemplates/CountryViewModel.cshtml:

@model CountryViewModel

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

O acréscimo de elementos HTML <option> não está limitado ao caso de No selection. Por exemplo, o seguinte método de ação e exibição gerarão HTML semelhante ao código acima:

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>

O elemento <option> correto será selecionado (contém o atributo selected="selected") dependendo do valor atual 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 adicionais