Question: Select Tag Helper in MVC 6

Question

Select Tag Helper in MVC 6

Answers 2
Added at 2016-01-06 02:01
Tags
Question

I need some help with the select tag helper in MVC 6.

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.

Answers to

Select Tag Helper in MVC 6

nr: #1 dodano: 2016-01-06 02:01

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.

nr: #2 dodano: 2016-01-06 02:01

Using the Select Tag helpers to render a SELECT element in your view

In your GET action, create an object of your view model, load the EmployeeList collection property and send that to the view.

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

And in your create view, create a new SelectList object from the EmployeeList property and pass that as value for the asp-items property.

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

And your HttpPost action method to accept the submitted form data.

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

Or if your view model has a List<SelectListItem> as the property for your dropdown items.

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

And in your get 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);
}

And in the view, you can directly use the Employees property for the 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>

The class SelectListItem belongs to Microsoft.AspNet.Mvc.Rendering namespace.

Source Show
◀ Wstecz