J'ai besoin d'aide avec l'assistant de sélection de balises 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 un List<Employee> EmployeesList
et la valeur sélectionnée ira dans la propriété EmployeeId
. 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 la suivante: comment demander à mon assistant de sélection d'utiliser la valeur Id
comme valeur lors de l'affichage de FullName
dans la liste déroulante?
<select asp-for="EmployeeId" asp-items="???" />
J'apprécierais un peu d'aide avec ceci. Merci.
Dans votre action GET, créez un objet de votre modèle de vue, chargez la propriété de collection EmployeeList
et envoyez-le à 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 objet SelectList
à partir de la propriété EmployeeList
et transmettez-le comme valeur pour la propriété asp-items
.
@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 un List<SelectListItem>
comme propriété de vos éléments déroulants.
public class MyViewModel
{
public int EmployeeId { get; set; }
public string Comments { get; set; }
public List<SelectListItem> Employees { set; get; }
}
Et dans votre action get,
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 propriété Employees
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 SelectListItem
appartient à Microsoft.AspNet.Mvc.Rendering
namespace.
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!
L'approche ci-dessous ne fonctionnera pas
<select asp-for="EmployeeId" asp-items="@Model.Employees" />
Mais ça va marcher.
<select asp-for="EmployeeId" asp-items="@Model.Employees"></select>
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 ait une propriété appelée Employees
, de type DbSet<Employee>
où la classe d'entités Employee
a une propriété Id
et Name
comme 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'objets SelectListItem
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 context
est votre objet de contexte de base de données. Le code de vue est le même que ci-dessus.
Certaines personnes préfèrent utiliser la classe SelectList
pour contenir les éléments nécessaires au rendu des options.
public class MyViewModel
{
public int EmployeeId { get; set; }
public SelectList Employees { set; get; }
}
Désormais, dans votre action GET, vous pouvez utiliser le constructeur SelectList
pour renseigner la propriété Employees
du modèle de vue. Assurez-vous de spécifier les paramètres dataValueField
et 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" }
};
}
J'appelle ici la méthode GetEmployees
pour obtenir une liste d'objets Employee, chacun avec une propriété Id
et FirstName
et j'utilise ces propriétés comme DataValueField
et DataTextField
de l'objet SelectList
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>
Parfois, vous souhaiterez peut-être rendre un élément sélectionné à partir d'une liste de chaînes. Dans ce cas, vous pouvez utiliser le constructeur SelectList
qui ne prend que IEnumerable<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.
Il peut arriver que vous souhaitiez 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 de l'option précédemment enregistrée). Pour ce faire, vous pouvez simplement définir la valeur de la propriété EmployeeId
sur la valeur de l'option à 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 lors du rendu de la page.
Si vous souhaitez afficher une liste déroulante à sélections multiples, vous pouvez simplement changer la propriété de votre modèle de vue que vous utilisez pour l'attribut asp-for
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'attribut multiple
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>
Semblable à la sélection simple, définissez la valeur de la propriété EmployeeIds
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);
}
Ceci sélectionnera l'option Tom et Jerry dans l'élément de sélection multiple lors du rendu de la page.
Si vous préférez ne pas conserver une propriété de type collection pour transmettre la liste des 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 sujettes à des erreurs de frappe non corrigées)
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>
C'est pareil que ci-dessus. Tout ce que vous avez à faire est de définir la propriété (pour laquelle vous liez la liste déroulante) à 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>
La méthode d'assistance pour les balises de sélection prend en charge les options de regroupement dans une liste déroulante. Tout ce que vous avez à faire est de spécifier la valeur de la propriété Group
de chaque SelectListItem
dans 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 des balises va maintenant afficher les options dans 2 optgroup items.
J'ai créé une interface et un assistant <options>
pour cela. Je n'ai donc pas eu à convertir les éléments IEnumerable<T>
en IEnumerable<SelectListItem>
chaque fois que je dois renseigner le contrôle <select>
.
Et je pense que ça marche à 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 le tag helper, 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 ; }
}
Ce sont les codes nécessaires:
L'interface:
public interface IIntegerListItem
{
int Value { get; }
string Text { get; }
}
L'assistant de balise <options>
:
[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 l'objectif est clair, je pense. J'ai dû éditer un peu.
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;
vous pouvez utiliser le code ci-dessous pour 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>
En obtenir:
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>
Ma réponse ci-dessous ne ne résout pas la question mais elle concerne.
Si quelqu'un utilise enum
au lieu d'un modèle de classe, comme dans l'exemple suivant:
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 de 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 code 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>