I need some help with the select tag helper in ASP.NET Core.
I have a list of employees that I'm trying to bind to a select tag helper. My employees are in a List<Employee> EmployeesList
and selected value will go into EmployeeId
property. My view model looks like this:
public class MyViewModel
{
public int EmployeeId { get; set; }
public string Comments { get; set; }
public List<Employee> EmployeesList {get; set; }
}
My employee class looks like this:
public class Employee
{
public int Id { get; set; }
public string FullName { get; set; }
}
My question is how do I tell my select tag helper to use the Id
as the value while displaying FullName
in the drop down list?
<select asp-for="EmployeeId" asp-items="???" />
I'd appreciate some help with this. Thanks.
This question is related to
c#
asp.net-core
asp.net-core-mvc
tag-helpers
You can use below code for multiple select:
<select asp-for="EmployeeId" multiple="multiple" asp-items="@ViewBag.Employees">
<option>Please select</option>
</select>
You can also use:
<select id="EmployeeId" name="EmployeeId" multiple="multiple" asp-items="@ViewBag.Employees">
<option>Please select</option>
</select>
I created an Interface and a <options>
tag helper for this. So I didn't have to convert the IEnumerable<T>
items into IEnumerable<SelectListItem>
every time I have to populate the <select>
control.
And I think it works beautifully...
The usage is something like:
<select asp-for="EmployeeId">
<option value="">Please select...</option>
<options asp-items="@Model.EmployeesList" />
</select>
And to make it work with the tag helper you have to implement that interface in your class:
public class Employee : IIntegerListItem
{
public int Id { get; set; }
public string FullName { get; set; }
public int Value { return Id; }
public string Text{ return FullName ; }
}
These are the needed codes:
The interface:
public interface IIntegerListItem
{
int Value { get; }
string Text { get; }
}
The <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>");
}
}
}
}
There may be some typo but the aim is clear I think. I had to edit a little bit.
My answer below doesn't solve the question but it relates to.
If someone is using enum
instead of a class model, like this example:
public enum Counter
{
[Display(Name = "Number 1")]
No1 = 1,
[Display(Name = "Number 2")]
No2 = 2,
[Display(Name = "Number 3")]
No3 = 3
}
And a property to get the value when submiting:
public int No { get; set; }
In the razor page, you can use Html.GetEnumSelectList<Counter>()
to get the enum properties.
<select asp-for="No" asp-items="@Html.GetEnumSelectList<Counter>()"></select>
It generates the following HTML:
<select id="No" name="No">
<option value="1">Number 1</option>
<option value="2">Number 2</option>
<option value="3">Number 3</option>
</select>
You can also use 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;
In Get:
public IActionResult Create()
{
ViewData["Tags"] = new SelectList(_context.Tags, "Id", "Name");
return View();
}
In Post:
var selectedIds= Request.Form["Tags"];
In View :
<label>Tags</label>
<select asp-for="Tags" id="Tags" name="Tags" class="form-control" asp-items="ViewBag.Tags" multiple></select>
Source: Stackoverflow.com