Tag Helpers dans les formulaires dans ASP.NET Core

Par Rick Anderson, N. Taylor Mullen, Dave Paquette et Jerrie Pelser

Ce document montre comment utiliser les formulaires, ainsi que les éléments HTML couramment utilisés dans un formulaire. L’élément HTML Form fournit le principal mécanisme utilisé par les applications web pour publier des données sur le serveur. La majeure partie de ce document décrit les Tag Helpers et explique comment ils peuvent vous aider à créer des formulaires HTML robustes. Nous vous recommandons de lire Introduction aux Tag Helpers avant de lire ce document.

Dans de nombreux cas, les HTML Helpers offrent une autre approche par rapport à un Tag Helper spécifique. Toutefois, il est clair que les Tag Helpers ne remplacent pas les HTML Helpers, et qu’il n’existe pas toujours un Tag Helper pour chaque HTML Helper. Quand une alternative HTML Helper existe, elle est mentionnée.

Tag Helper Form

Form Tag Helper :

  • Génère la valeur de l’attribut HTML <FORM>action pour une action de contrôleur MVC ou une route nommée

  • Génère un jeton de vérification de requête masqué pour empêcher la falsification de requête intersites (quand il est utilisé avec l’attribut [ValidateAntiForgeryToken] dans la méthode d’action HTTP Post)

  • Fournit l’attribut asp-route-<Parameter Name>, où <Parameter Name> est ajouté aux valeurs de routage. Les paramètres routeValues pour Html.BeginForm et Html.BeginRouteForm fournissent des fonctionnalités similaires.

  • Comporte une alternative HTML Helper avec Html.BeginForm et Html.BeginRouteForm

Exemple :

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

Le Tag Helper Form ci-dessus génère le code HTML suivant :

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

Le runtime MVC génère la valeur de l’attribut action à partir des attributs asp-controller et asp-action du Tag Helper Form. Le Tag Helper Form génère également un jeton de vérification de requête masqué pour empêcher la falsification de requête intersites (quand il est utilisé avec l’attribut [ValidateAntiForgeryToken] dans la méthode d’action HTTP Post). Il est difficile de protéger un formulaire HTML contre une falsification de requête intersites. Le Tag Helper Form se charge de fournir ce service à votre place.

Utilisation d’un routage nommé

L’attribut Tag Helper asp-route peut également générer des balises pour l’attribut HTML action. Une application avec une route nommée register pourrait utiliser le balisage suivant pour la page d’inscription :

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

Un bon nombre des vues du dossier Vues/Compte (généré quand vous créez une application web avec des comptes d’utilisateurs individuels) contiennent l’attribut asp-route-returnurl :

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

Notes

Avec les modèles intégrés, returnUrl est uniquement rempli automatiquement quand vous essayez d’accéder à une ressource autorisée sans l’authentification ou l’autorisation nécessaire. Quand vous tentez d’effectuer un accès non autorisé, l’intergiciel (middleware) de sécurité vous redirige vers la page de connexion avec le returnUrl défini.

Le Tag Helper Form Action

Le Tag Helper Form Action génère l’attribut formaction sur la balise <button ...> ou <input type="image" ...> générée. L’attribut formaction détermine où un formulaire envoie ses données. Il se lie aux éléments <input> de type image et aux éléments <button>. Le Tag Helper Form Action permet l’utilisation de plusieurs attributs AnchorTagHelperasp- pour contrôler quel lien formaction est généré pour l’élément correspondant.

Attributs AnchorTagHelper pris en charge pour contrôler la valeur de formaction :

Attribut Description
asp-controller Nom du contrôleur.
asp-action Nom de la méthode d’action.
asp-area Nom de la zone.
asp-page Nom de la page Razor.
asp-page-handler Nom du gestionnaire de page Razor.
asp-route Nom de l'itinéraire.
asp-route-{value} Valeur de routage d’URL unique. Par exemple : asp-route-id="1234".
asp-all-route-data Toutes les valeurs d’itinéraire.
asp-fragment Fragment d’URL.

Exemple d’envoi au contrôleur

Le balisage suivant envoie le formulaire à l’action Index de HomeController lorsque input ou button est sélectionnée :

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

Le balisage précédent génère le code HTML suivant :

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

Exemple d’envoi à la page

Le balisage suivant envoie le formulaire à la page RazorAbout :

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

Le balisage précédent génère le code HTML suivant :

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

Exemple d’envoi à l’itinéraire

Prenez le point de terminaison /Home/Test :

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

Le balisage suivant envoie le formulaire au point de terminaison /Home/Test.

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

Le balisage précédent génère le code HTML suivant :

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

Tag Helper Input

Le Tag Helper Input lie un élément HTML <input>> à une expression de modèle dans votre vue Razor.

Syntaxe :

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

Tag Helper Input :

  • Génère les attributs HTML id et name pour le nom d’expression spécifié dans l’attribut asp-for. asp-for="Property1.Property2" équivaut à m => m.Property1.Property2. Nom de l’expression utilisée pour la valeur de l’attribut asp-for. Pour plus d’informations, consultez la section Noms d’expressions.

  • Définit la valeur de l’attribut HTML type en fonction du type de modèle et des attributs d’annotation de données appliqués à la propriété de modèle

  • Ne remplace pas la valeur de l’attribut HTML type quand une valeur est spécifiée

  • Génère des attributs de validation HTML5 à partir des attributs d’annotation de données appliqués aux propriétés de modèle

  • Chevauche des fonctionnalités HTML Helper avec Html.TextBoxFor et Html.EditorFor. Pour plus d’informations, consultez la section Alternatives HTML Helper au Tag Helper Input.

  • Fournit un typage fort. Si le nom de la propriété change et si vous ne mettez pas à jour le Tag Helper, vous obtenez une erreur similaire à celle-ci :

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

Le Tag Helper Input définit l’attribut HTML type en fonction du type .NET. Le tableau suivant liste certains types .NET usuels et le type HTML généré (tous les types .NET ne sont pas listés).

Type .NET Type d’entrée
Bool type="checkbox"
String type="text"
DateTime type="datetime-local"
Byte type="number"
Int type="number"
Single, Double type="number"

Le tableau suivant présente des attributs d’annotations de données usuels que le Tag Helper Input mappe à des types d’entrée spécifiques (tous les attributs de validation ne sont pas listés) :

Attribut Type d’entrée
[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"

Exemple :

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>

Le code ci-dessus génère le code HTML suivant :

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

Les annotations de données appliquées aux propriétés Email et Password génèrent des métadonnées pour le modèle. Le Tag Helper Input consomme les métadonnées du modèle et génère les attributs HTML5data-val-* (consultez Validation de modèle). Ces attributs décrivent les validateurs à attacher aux champs d’entrée. Cela permet d’effectuer une validation HTML5 et jQuery discrète. Les attributs discrets ont le format data-val-rule="Error Message", rule étant le nom de la règle de validation (comme data-val-required, data-val-email, data-val-maxlength, etc.). Si un message d’erreur est fourni dans l’attribut, il s’affiche en tant que valeur de l’attribut data-val-rule. Il existe également des attributs ayant la forme data-val-ruleName-argumentName="argumentValue" et qui fournissent des détails supplémentaires sur la règle, par exemple data-val-maxlength-max="1024".

Lorsque plusieurs contrôles input sont liés à une même propriété, les contrôles générés partagent le même id, ce qui rend le balisage généré non valide. Pour éviter les doublons, spécifiez explicitement l’attribut id pour chaque contrôle.

Rendu d’entrée masquée pour les cases à cocher

Les cases à cocher en HTML5 n’envoient pas de valeur lorsqu’elles sont décochées. Pour permettre l’envoi d’une valeur par défaut pour une case à cocher non cochée, le Tag Helper Input génère une entrée masquée supplémentaire pour les cases à cocher.

Prenons l’exemple du balisage Razor suivant qui utilise le Tag Helper Input pour une propriété de modèle booléenne IsChecked :

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

Le balisage Razor précédent génère un balisage HTML qui se présente comme suit :

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

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

Le balisage HTML précédent présente une entrée masquée supplémentaire nommée IsChecked dont la valeur est false. Par défaut, cette entrée masquée est affichée à la fin du formulaire. Lorsque le formulaire est envoyé :

  • Si l’entrée de case à cocher IsChecked est cochée, true et false sont envoyés en tant que valeurs.
  • Si l’entrée de case à cocher IsChecked est décochée, seule la valeur d’entrée masquée false est envoyée.

Le processus de liaison de modèle ASP.NET Core lit uniquement la première valeur lors de la liaison à une valeur bool, ce qui donne true pour les cases à cocher cochées et false pour les cases à cocher non cochées.

Pour configurer le comportement du rendu d’entrée masquée, définissez la propriété CheckBoxHiddenInputRenderMode dans MvcViewOptions.HtmlHelperOptions. Par exemple :

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

Le code précédent désactive le rendu d’entrée masquée pour les cases à cocher en définissant CheckBoxHiddenInputRenderMode sur CheckBoxHiddenInputRenderMode.None. Pour connaître tous les modes de rendu disponibles, consultez l’énumération CheckBoxHiddenInputRenderMode.

Alternatives HTML Helper au Tag Helper Input

Html.TextBox, Html.TextBoxFor, Html.Editor et Html.EditorFor ont des fonctionnalités qui chevauchent celles du Tag Helper Input. Le Tag Helper Input définit automatiquement l’attribut type, contrairement à Html.TextBox et Html.TextBoxFor. Html.Editor et Html.EditorFor gèrent les collections, les objets complexes et les modèles, contrairement au Tag Helper Input. Le Tag Helper Input, Html.EditorFor et Html.TextBoxFor sont fortement typés (ils utilisent des expressions lambda), contrairement à Html.TextBox et Html.Editor (qui utilisent des noms d’expression).

HtmlAttributes

@Html.Editor() et @Html.EditorFor() utilisent une entrée ViewDataDictionary spéciale nommée htmlAttributes durant l’exécution de leurs modèles par défaut. Ce comportement est éventuellement amélioré à l’aide des paramètres additionalViewData. La clé « htmlAttributes » ne respecte pas la casse. La clé « htmlAttributes » est prise en charge de manière similaire à l’objet htmlAttributes passé aux Helpers d’entrée tels que @Html.TextBox().

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

Noms d’expressions

La valeur de l’attribut asp-for est un ModelExpression et correspond au côté droit d’une expression lambda. Ainsi, asp-for="Property1" devient m => m.Property1 dans le code généré, ce qui explique pourquoi vous n’avez pas besoin de le faire commencer par Model. Vous pouvez utiliser le caractère « @ » pour débuter une expression inline avant m. :

@{
  var joe = "Joe";
}

<input asp-for="@joe">

Génère ce qui suit :

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

Avec les propriétés de collection, asp-for="CollectionProperty[23].Member" génère le même nom que asp-for="CollectionProperty[i].Member" quand i a la valeur 23.

Quand ASP.NET Core MVC calcule la valeur de ModelExpression, plusieurs sources sont inspectées, notamment ModelState. Prenez le cas de <input type="text" asp-for="Name">. L’attribut value calculé est la première valeur non-null des éléments suivants :

  • Entrée ModelState avec la clé « Name ».
  • Résultat de l’expression Model.Name.

Vous pouvez également accéder aux propriétés enfants à l’aide du chemin de propriété du modèle de vue. Prenons le cas d’une classe de modèle plus complexe qui contient une propriété enfant 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; }
}

Dans la vue, nous effectuons une liaison à 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>

Le code HTML suivant est généré pour Address.AddressLine1 :

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

Noms d’expressions et collections

Dans cet exemple, un modèle contient un tableau de Colors :

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

    public int Age { get; set; }
}

Méthode d’action :

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

Le code Razor suivant montre comment accéder à un élément Color spécifique :

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

Le modèle Views/Shared/EditorTemplates/String.cshtml :

@model string

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

Exemple utilisant List<T> :

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

    public bool IsDone { get; set; }
}

Le code Razor suivant montre comment itérer sur une collection :

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

Le modèle 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>
*@

Utilisez si possible foreach quand la valeur doit être employée dans un contexte équivalent à asp-for ou Html.DisplayFor. En règle générale, préférez for à foreach (si le scénario le permet), car il n’a pas besoin d’allouer un énumérateur. Toutefois, l’évaluation d’un indexeur dans une expression LINQ peut s’avérer coûteuse et doit être réduite.

 

Notes

L’exemple de code commenté ci-dessus montre comment remplacer l’expression lambda par l’opérateur @ pour accéder à chaque ToDoItem dans la liste.

Tag Helper Textarea

Le Tag Helper Textarea Tag Helper est similaire au Tag Helper Input.

  • Génère les attributs id et name, ainsi que les attributs de validation des données du modèle pour un élément <textarea>>.

  • Fournit un typage fort.

  • Alternative HTML Helper : Html.TextAreaFor

Exemple :

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>

Le code HTML suivant est généré :

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

Tag Helper Label

  • Génère la légende d’étiquette et l’attribut for d’un élément <label>> pour un nom d’expression

  • Alternative HTML Helper : Html.LabelFor.

Le Label Tag Helper offre les avantages suivants par rapport à un élément d’étiquette HTML pur :

  • Vous obtenez automatiquement la valeur d’étiquette descriptive à partir de l’attribut Display. Le nom d’affichage prévu peut changer plus tard. La combinaison de l’attribut Display et du Tag Helper Label applique Display partout où il est utilisé.

  • Moins de balises dans le code source

  • Typage fort avec la propriété de modèle.

Exemple :

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>

Le code HTML suivant est généré pour l’élément <label> :

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

Le Tag Helper Label a généré la valeur « Email » pour l’attribut for, qui représente l’ID associé à l’élément <input>. Les Tag Helpers génèrent des éléments id et for cohérents pour qu’ils puissent être correctement associés. La légende de cet exemple provient de l’attribut Display. Si le modèle ne contient pas d’attribut Display, la légende correspond au nom de propriété de l’expression. Pour remplacer la légende par défaut, ajoutez une légende à l’intérieur de la balise label (étiquette).

Tag Helpers Validation

Il existe deux Tag Helpers Validation. Le Validation Message Tag Helper (qui affiche un message de validation pour une seule propriété de votre modèle) et le Validation Summary Tag Helper (qui affiche un récapitulatif des erreurs de validation). Le Input Tag Helper ajoute des attributs de validation HTML5 côté client aux éléments d’entrée en fonction des attributs d’annotation de données pour vos classes de modèle. La validation est également effectuée sur le serveur. Le Tag Helper Validation affiche ces messages d’erreur quand une erreur de validation se produit.

Le Tag Helper Validation Message

  • Ajoute l’attribut HTML5data-valmsg-for="property" à l’élément span, qui attache les messages d’erreur de validation du champ d’entrée de la propriété de modèle spécifiée. Quand une erreur de validation côté client se produit, jQuery affiche le message d’erreur dans l’élément <span>.

  • La validation a également lieu sur le serveur. Il arrive que JavaScript soit désactivé sur les clients et qu’une partie de la validation puisse être effectuée uniquement côté serveur.

  • Alternative HTML Helper : Html.ValidationMessageFor

Le Validation Message Tag Helper est utilisé avec l’attribut asp-validation-for sur un élément HTML span.

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

Le Tag Helper Validation Message génère le code HTML suivant :

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

En règle générale, vous utilisez le Validation Message Tag Helper après un Tag Helper Input pour la même propriété. Dans ce cas, les messages d’erreur de validation s’affichent près de l’entrée qui a provoqué l’erreur.

Notes

Vous devez avoir une vue avec les références de script JavaScript et jQuery appropriées pour la validation côté client. Pour plus d’informations, consultez Validation de modèle.

Quand une erreur de validation côté serveur se produit (par exemple, quand vous disposez d’une validation personnalisée côté serveur ou quand la validation côté client est désactivée), MVC place ce message d’erreur dans le corps de l’élément <span>.

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

Le Tag Helper Validation Summary

  • Cible les éléments <div> avec les attributs asp-validation-summary

  • Alternative HTML Helper : @Html.ValidationSummary

Le Validation Summary Tag Helper est utilisé pour afficher un récapitulatif des messages de validation. La valeur de l’attribut asp-validation-summary peut correspondre à l’une des valeurs suivantes :

asp-validation-summary Messages de validation affichés
All Niveau de la propriété et du modèle
ModelOnly Modèle
None None

Exemple

Dans l’exemple suivant, le modèle de données possède des attributs DataAnnotation, ce qui génère des messages d’erreur de validation pour l’élément <input>. Quand une erreur de validation se produit, le Tag Helper Validation affiche le message d’erreur :

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>

Code HTML généré (quand le modèle est valide) :

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

Tag Helper Select

  • Génère l’élément select et les éléments option associés pour les propriétés de votre modèle.

  • Comporte une alternative HTML Helper avec Html.DropDownListFor et Html.ListBoxFor

Le Select Tag Helperasp-for spécifie le nom de propriété de modèle de l’élément select, et asp-items spécifie les éléments option. Par exemple :

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

Exemple :

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

La méthode Index initialise CountryViewModel, définit le pays sélectionné et le passe à la vue Index.

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

La méthode HTTP POST Index affiche la sélection :

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

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

Qui génère le code HTML suivant (avec « CA » sélectionné) :

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

Notes

Nous déconseillons d’utiliser ViewBag ou ViewData avec le Tag Helper Select. Un modèle de vue est plus robuste et, en général, moins problématique pour fournir des métadonnées MVC.

La valeur de l’attribut asp-for est un cas particulier et ne nécessite pas de préfixe Model, contrairement aux autres attributs du Tag Helper (par exemple asp-items)

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

Liaison d’enum

Il est souvent pratique d’utiliser <select> avec une propriété enum et de générer les éléments SelectListItem à partir des valeurs de enum.

Exemple :

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

La méthode GetEnumSelectList génère un objet SelectList pour un enum.

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

Vous pouvez marquer votre liste d’énumérateurs avec l’attribut Display pour obtenir une IU plus riche :

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

Le code HTML suivant est généré :

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

Groupe d’options

L’élément HTML <optgroup>> est généré quand le modèle de vue contient un ou plusieurs objets SelectListGroup.

CountryViewModelGroup regroupe les éléments SelectListItem dans les groupes « North America » et « 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; }

Les deux groupes sont affichés ci-dessous :

option group example

Code HTML généré :

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

Sélection multiple

Le Tag Helper Select génère automatiquement l’attribut multiple = "multiple" si la propriété spécifiée dans l’attribut asp-for est IEnumerable. Par exemple, le modèle suivant :

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

Avec la vue suivante :

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

Génère le code HTML suivant :

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

Aucune sélection

Si vous constatez que l’option « not specified » est utilisée dans plusieurs pages, vous pouvez créer un modèle pour éviter de répéter le code HTML :

@model CountryViewModel

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

Le modèle Views/Shared/EditorTemplates/CountryViewModel.cshtml :

@model CountryViewModel

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

L’ajout d’éléments HTML <option>> ne se limite pas au cas Pas de sélection. Par exemple, la vue et la méthode d’action suivante génèrent du code HTML similaire au code ci-dessus :

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>

L’élément <option> approprié est sélectionné (il contient l’attribut selected="selected") en fonction de la valeur actuelle 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>

Ressources supplémentaires