Sélectionnez Tag Helper dans ASP.NET Core MVC

162

J'ai besoin d'aide avec l'assistant de sélection de balise dans ASP.NET Core.

J'ai une liste d'employés que j'essaie de lier à un assistant de sélection de balises. Mes employés sont dans une List<Employee> EmployeesListvaleur sélectionnée et iront dans la EmployeeIdpropriété. Mon modèle de vue ressemble à ceci:

public class MyViewModel
{
   public int EmployeeId { get; set; }
   public string Comments { get; set; }
   public List<Employee> EmployeesList {get; set; }
}

Ma classe d'employés ressemble à ceci:

public class Employee
{
   public int Id { get; set; }
   public string FullName { get; set; }
}

Ma question est de savoir comment dire à mon assistant de sélection de balise d'utiliser le Idcomme valeur lors de l'affichage FullNamedans la liste déroulante?

<select asp-for="EmployeeId" asp-items="???" />

J'apprécierais de l'aide avec ceci. Merci.

Sam
la source
3
juste quelque chose que je pensais devoir ajouter, cela ne semble pas fonctionner si vous fermez immédiatement la balise select, fermez toujours les balises avec </select> l'assistant de balise ne fonctionnait pas avec <select asp-for ..... />
RoughPlace
juste un conseil. Les contrôleurs d'échafaudage vous montrent généralement les meilleures façons de faire de telles choses
Neville Nazerane

Réponses:

380

Utilisation des assistants Select Tag pour rendre un élément SELECT

Dans votre action GET, créez un objet de votre modèle de vue, chargez la EmployeeListpropriété de collection et envoyez-la à la vue.

public IActionResult Create()
{
    var vm = new MyViewModel();
    vm.EmployeesList = new List<Employee>
    {
        new Employee { Id = 1, FullName = "Shyju" },
        new Employee { Id = 2, FullName = "Bryan" }
    };
    return View(vm);
}

Et dans votre vue de création, créez un nouvel SelectListobjet à partir de la EmployeeListpropriété et transmettez-le comme valeur pour la asp-itemspropriété.

@model MyViewModel
<form asp-controller="Home" asp-action="Create">

    <select asp-for="EmployeeId" 
            asp-items="@(new SelectList(Model.EmployeesList,"Id","FullName"))">
        <option>Please select one</option>
    </select>

    <input type="submit"/>

</form>

Et votre méthode d'action HttpPost pour accepter les données de formulaire soumises.

[HttpPost]
public IActionResult Create(MyViewModel model)
{
   //  check model.EmployeeId 
   //  to do : Save and redirect
}

Ou

Si votre modèle de vue a une List<SelectListItem>comme propriété pour vos éléments de liste déroulante.

public class MyViewModel
{
    public int EmployeeId { get; set; }
    public string Comments { get; set; }
    public List<SelectListItem> Employees { set; get; }
}

Et dans ton action,

public IActionResult Create()
{
    var vm = new MyViewModel();
    vm.Employees = new List<SelectListItem>
    {
        new SelectListItem {Text = "Shyju", Value = "1"},
        new SelectListItem {Text = "Sean", Value = "2"}
    };
    return View(vm);
}

Et dans la vue, vous pouvez directement utiliser la Employeespropriété pour le asp-items.

@model MyViewModel
<form asp-controller="Home" asp-action="Create">

    <label>Comments</label>
    <input type="text" asp-for="Comments"/>

    <label>Lucky Employee</label>
    <select asp-for="EmployeeId" asp-items="@Model.Employees" >
        <option>Please select one</option>
    </select>

    <input type="submit"/>

</form>

La classe SelectListItemappartient à l' Microsoft.AspNet.Mvc.Renderingespace de noms.

Assurez-vous que vous utilisez une balise de fermeture explicite pour l'élément de sélection. Si vous utilisez l'approche de balise à fermeture automatique, l'assistant de balise rendra un élément SELECT vide!

L'approche ci-dessous ne fonctionnera pas

<select asp-for="EmployeeId" asp-items="@Model.Employees" />

Mais cela fonctionnera.

<select asp-for="EmployeeId" asp-items="@Model.Employees"></select>

Obtenir des données de votre table de base de données à l'aide de la structure d'entité

Les exemples ci-dessus utilisent des éléments codés en dur pour les options. J'ai donc pensé ajouter un exemple de code pour obtenir des données à l'aide du framework Entity, car beaucoup de gens l'utilisent.

Supposons que votre objet DbContext a une propriété appelée Employees, qui est de type DbSet<Employee>où la Employeeclasse d'entité a une propriété Idand Namecomme celle-ci

public class Employee
{
   public int Id { set; get; }
   public string Name { set; get; }
}

Vous pouvez utiliser une requête LINQ pour obtenir les employés et utiliser la méthode Select dans votre expression LINQ pour créer une liste d' SelectListItemobjets pour chaque employé.

public IActionResult Create()
{
    var vm = new MyViewModel();
    vm.Employees = context.Employees
                          .Select(a => new SelectListItem() {  
                              Value = a.Id.ToString(),
                              Text = a.Name
                          })
                          .ToList();
    return View(vm);
}

En supposant que contextvotre objet de contexte db. Le code de vue est le même que ci-dessus.

Utilisation de SelectList

Certaines personnes préfèrent utiliser la SelectListclasse pour contenir les éléments nécessaires au rendu des options.

public class MyViewModel
{
    public int EmployeeId { get; set; }
    public SelectList Employees { set; get; }
}

Maintenant, dans votre action GET, vous pouvez utiliser le SelectListconstructeur pour renseigner la Employeespropriété du modèle de vue. Assurez-vous de spécifier les paramètres dataValueFieldet dataTextField.

public IActionResult Create()
{
    var vm = new MyViewModel();
    vm.Employees = new SelectList(GetEmployees(),"Id","FirstName");
    return View(vm);
}
public IEnumerable<Employee> GetEmployees()
{
    // hard coded list for demo. 
    // You may replace with real data from database to create Employee objects
    return new List<Employee>
    {
        new Employee { Id = 1, FirstName = "Shyju" },
        new Employee { Id = 2, FirstName = "Bryan" }
    };
}

Ici , je fais appel à la GetEmployeesméthode pour obtenir une liste des objets employés, chacun avec une Idet FirstNamepropriété et j'utiliser ces propriétés DataValueFieldet DataTextFieldde l' SelectListobjet que nous avons créé. Vous pouvez modifier la liste codée en dur en un code qui lit les données d'une table de base de données.

Le code de vue sera le même.

<select asp-for="EmployeeId" asp-items="@Model.Employees" >
    <option>Please select one</option>
</select>

Rendu un élément SELECT à partir d'une liste de chaînes.

Parfois, vous souhaiterez peut-être rendre un élément de sélection à partir d'une liste de chaînes. Dans ce cas, vous pouvez utiliser le SelectListconstructeur qui ne prend queIEnumerable<T>

var vm = new MyViewModel();
var items = new List<string> {"Monday", "Tuesday", "Wednesday"};
vm.Employees = new SelectList(items);
return View(vm);

Le code de vue sera le même.

Définition des options sélectionnées

Parfois, vous souhaiterez peut-être définir une option comme option par défaut dans l'élément SELECT (par exemple, dans un écran d'édition, vous souhaitez charger la valeur d'option précédemment enregistrée). Pour ce faire, vous pouvez simplement définir la EmployeeIdvaleur de la propriété sur la valeur de l'option que vous souhaitez sélectionner.

public IActionResult Create()
{
    var vm = new MyViewModel();
    vm.Employees = new List<SelectListItem>
    {
        new SelectListItem {Text = "Shyju", Value = "11"},
        new SelectListItem {Text = "Tom", Value = "12"},
        new SelectListItem {Text = "Jerry", Value = "13"}
    };
    vm.EmployeeId = 12;  // Here you set the value
    return View(vm);
}

Cela sélectionnera l'option Tom dans l'élément de sélection lorsque la page est rendue.

Liste déroulante de sélection multiple

Si vous souhaitez rendre une liste déroulante à sélection multiple, vous pouvez simplement modifier la propriété de votre modèle de vue que vous utilisez pour l' asp-forattribut dans votre vue en un type de tableau.

public class MyViewModel
{
    public int[] EmployeeIds { get; set; }
    public List<SelectListItem> Employees { set; get; }
}

Cela rendra le balisage HTML pour l'élément select avec l' multipleattribut qui permettra à l'utilisateur de sélectionner plusieurs options.

@model MyViewModel
<select id="EmployeeIds" multiple="multiple" name="EmployeeIds">
    <option>Please select one</option>
    <option value="1">Shyju</option>
    <option value="2">Sean</option>
</select>

Définition des options sélectionnées dans la sélection multiple

Similaire à la sélection unique, définissez la EmployeeIdsvaleur de la propriété sur le tableau de valeurs souhaité.

public IActionResult Create()
{
    var vm = new MyViewModel();
    vm.Employees = new List<SelectListItem>
    {
        new SelectListItem {Text = "Shyju", Value = "11"},
        new SelectListItem {Text = "Tom", Value = "12"},
        new SelectListItem {Text = "Jerry", Value = "13"}
    };
    vm.EmployeeIds= new int[] { 12,13} ;  
    return View(vm);
}

Cela sélectionnera l'option Tom et Jerry dans l'élément de sélection multiple lorsque la page est rendue.

Utilisation de ViewBag pour transférer la liste des articles

Si vous ne préférez pas conserver une propriété de type collection pour transmettre la liste d'options à la vue, vous pouvez utiliser le ViewBag dynamique pour le faire. ( Ce n'est pas mon approche personnellement recommandée car viewbag est dynamique et votre code est susceptible de ne pas être corrigé. fautes de frappe )

public IActionResult Create()
{       
    ViewBag.Employees = new List<SelectListItem>
    {
        new SelectListItem {Text = "Shyju", Value = "1"},
        new SelectListItem {Text = "Sean", Value = "2"}
    };
    return View(new MyViewModel());
}

et dans la vue

<select asp-for="EmployeeId" asp-items="@ViewBag.Employees">
    <option>Please select one</option>
</select>

Utilisation de ViewBag pour transférer la liste des éléments et paramétrer l'option sélectionnée

C'est la même chose que ci-dessus. Tout ce que vous avez à faire est de définir la valeur de la propriété (pour laquelle vous liez la liste déroulante) sur la valeur de l'option que vous souhaitez sélectionner.

public IActionResult Create()
{       
    ViewBag.Employees = new List<SelectListItem>
    {
        new SelectListItem {Text = "Shyju", Value = "1"},
        new SelectListItem {Text = "Bryan", Value = "2"},
        new SelectListItem {Text = "Sean", Value = "3"}
    };

    vm.EmployeeId = 2;  // This will set Bryan as selected

    return View(new MyViewModel());
}

et dans la vue

<select asp-for="EmployeeId" asp-items="@ViewBag.Employees">
    <option>Please select one</option>
</select>

Regrouper des éléments

La méthode d'aide à la sélection de balises prend en charge les options de regroupement dans une liste déroulante. Tout ce que vous avez à faire est de spécifier la Groupvaleur de propriété de chacun SelectListItemdans votre méthode d'action.

public IActionResult Create()
{
    var vm = new MyViewModel();

    var group1 = new SelectListGroup { Name = "Dev Team" };
    var group2 = new SelectListGroup { Name = "QA Team" };

    var employeeList = new List<SelectListItem>()
    {
        new SelectListItem() { Value = "1", Text = "Shyju", Group = group1 },
        new SelectListItem() { Value = "2", Text = "Bryan", Group = group1 },
        new SelectListItem() { Value = "3", Text = "Kevin", Group = group2 },
        new SelectListItem() { Value = "4", Text = "Alex", Group = group2 }
    };
    vm.Employees = employeeList;
    return View(vm);
}

Il n'y a pas de changement dans le code de vue. l'assistant de sélection de balises rendra désormais les options à l'intérieur de 2 éléments optgroup .

Shyju
la source
1
Je vous remercie! Un peu comme l'ancienne syntaxe de rasoir où nous devons effectuer une conversion vers SelectList. Merci encore.
Sam
7
Je cherchais comment ajouter une option vide, merci d'utiliser<option>Please select one</option>
Serj Sagan
5
Notez que vous DEVEZ avoir une balise de fermeture </select> - cela ne fonctionnera pas si vous essayez de fermer automatiquement la balise de sélection comme <select ... />. De plus, si vous utilisez une option vide telle que "Veuillez en sélectionner une", vous devez lui donner une valeur de chaîne vide "" pour que la validation du champ requis fonctionne.
Andy
3
"Assurez-vous que vous utilisez une balise de fermeture explicite pour l'élément select. Si vous utilisez l'approche de balise à fermeture automatique, l'assistant de balise rendra un élément SELECT vide!" C'était le problème dans mon cas. À votre santé!
Mariusz
2
Merci d'avoir mis tant d'efforts dans cette réponse!
Vidar
11

J'ai créé une interface et un <options>tag helper pour cela. Je n'ai donc pas eu à convertir les IEnumerable<T>éléments en IEnumerable<SelectListItem>chaque fois que je dois remplir le <select>contrôle.

Et je pense que cela fonctionne à merveille ...

L'utilisation est quelque chose comme:

<select asp-for="EmployeeId">
    <option value="">Please select...</option>
    <options asp-items="@Model.EmployeesList" />
</select>

Et pour que cela fonctionne avec l'assistant de balise, vous devez implémenter cette interface dans votre classe:

public class Employee : IIntegerListItem
{
   public int Id { get; set; }
   public string FullName { get; set; }

   public int Value { return Id; }
   public string Text{ return FullName ; }
}

Voici les codes nécessaires:

L'interface:

public interface IIntegerListItem
{
    int Value { get; }
    string Text { get; }
}

Le <options>tag helper:

[HtmlTargetElement("options", Attributes = "asp-items")]
public class OptionsTagHelper : TagHelper
{
    public OptionsTagHelper(IHtmlGenerator generator)
    {
        Generator = generator;
    }

    [HtmlAttributeNotBound]
    public IHtmlGenerator Generator { get; set; }

    [HtmlAttributeName("asp-items")]
    public object Items { get; set; }

    public override void Process(TagHelperContext context, TagHelperOutput output)
    {
        output.SuppressOutput();
        // Is this <options /> element a child of a <select/> element the SelectTagHelper targeted?
        object formDataEntry;
        context.Items.TryGetValue(typeof(SelectTagHelper), out formDataEntry);

        var selectedValues = formDataEntry as ICollection<string>;
        var encodedValues = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
        if (selectedValues != null && selectedValues.Count != 0)
        {
            foreach (var selectedValue in selectedValues)
            {
                encodedValues.Add(Generator.Encode(selectedValue));
            }
        }

        IEnumerable<SelectListItem> items = null;
        if (Items != null)
        {
            if (Items is IEnumerable)
            {
                var enumerable = Items as IEnumerable;
                if (Items is IEnumerable<SelectListItem>)
                    items = Items as IEnumerable<SelectListItem>;
                else if (Items is IEnumerable<IIntegerListItem>)
                    items = ((IEnumerable<IIntegerListItem>)Items).Select(x => new SelectListItem() { Selected = false, Value = ((IIntegerListItem)x).Value.ToString(), Text = ((IIntegerListItem)x).Text });
                else
                    throw new InvalidOperationException(string.Format("The {2} was unable to provide metadata about '{1}' expression value '{3}' for <options>.",
                        "<options>",
                        "ForAttributeName",
                        nameof(IModelMetadataProvider),
                        "For.Name"));
            }
            else
            {
                throw new InvalidOperationException("Invalid items for <options>");
            }

            foreach (var item in items)
            {
                bool selected = (selectedValues != null && selectedValues.Contains(item.Value)) || encodedValues.Contains(item.Value);
                var selectedAttr = selected ? "selected='selected'" : "";

                if (item.Value != null)
                    output.Content.AppendHtml($"<option value='{item.Value}' {selectedAttr}>{item.Text}</option>");
                else
                    output.Content.AppendHtml($"<option>{item.Text}</option>");
            }
        }
    }
}

Il y a peut-être une faute de frappe mais le but est clair je pense. J'ai dû modifier un peu.

Yves
la source
4

Vous pouvez également utiliser IHtmlHelper.GetEnumSelectList.

    // Summary:
    //     Returns a select list for the given TEnum.
    //
    // Type parameters:
    //   TEnum:
    //     Type to generate a select list for.
    //
    // Returns:
    //     An System.Collections.Generic.IEnumerable`1 containing the select list for the
    //     given TEnum.
    //
    // Exceptions:
    //   T:System.ArgumentException:
    //     Thrown if TEnum is not an System.Enum or if it has a System.FlagsAttribute.
    IEnumerable<SelectListItem> GetEnumSelectList<TEnum>() where TEnum : struct;
Amir Rezaei
la source
4

Ma réponse ci - dessous ne résout pas la question mais elle se rapporte à.

Si quelqu'un utilise à la enumplace d'un modèle de classe, comme cet exemple:

public enum Counter
{
    [Display(Name = "Number 1")]
    No1 = 1,
    [Display(Name = "Number 2")]
    No2 = 2,
    [Display(Name = "Number 3")]
    No3 = 3
}

Et une propriété pour obtenir la valeur lors de la soumission:

public int No { get; set; }

Dans la page rasoir, vous pouvez utiliser Html.GetEnumSelectList<Counter>()pour obtenir les propriétés enum.

<select asp-for="No" asp-items="@Html.GetEnumSelectList<Counter>()"></select>

Il génère le HTML suivant:

<select id="No" name="No">
    <option value="1">Number 1</option>
    <option value="2">Number 2</option>
    <option value="3">Number 3</option>
</select>
Bronzer
la source
3

Vous pouvez utiliser le code ci-dessous pour une sélection multiple :

<select asp-for="EmployeeId"  multiple="multiple" asp-items="@ViewBag.Employees">
    <option>Please select</option>
</select>

Vous pouvez aussi utiliser:

<select id="EmployeeId" name="EmployeeId"  multiple="multiple" asp-items="@ViewBag.Employees">
    <option>Please select</option>
</select>
Zubayer Bin Ayub
la source
0

Dans Get:

public IActionResult Create()
{
    ViewData["Tags"] = new SelectList(_context.Tags, "Id", "Name");
    return View();
}

En poste:

var selectedIds= Request.Form["Tags"];

En vue :

<label>Tags</label>
<select  asp-for="Tags"  id="Tags" name="Tags" class="form-control" asp-items="ViewBag.Tags" multiple></select>
Anas Al-Qudah
la source