在ASP.NET Core MVC中选择标签帮助程序

232

我需要在ASP.NET Core中使用选择标签助手的一些帮助。

我有一个雇员列表,我试图将其绑定到选择标记助手上。我的雇员在列表 List<Employee> EmployeesList 中,并且所选值将进入 EmployeeId 属性。我的视图模型如下所示:

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

我的员工类看起来像这样:

public class Employee
{
   public int Id { get; set; }
   public string FullName { get; set; }
}
我的问题是如何告诉我的选择标签助手在下拉列表中显示FullName,同时使用Id作为值?
<select asp-for="EmployeeId" asp-items="???" />

我需要帮助,谢谢。


4
只是我想补充一点,如果您立即关闭选择标签,则似乎无法正常工作,请始终使用</select>关闭标签,标签助手无法与<select asp-for...../>一起使用。 - RoughPlace
1
只是一个提示。脚手架控制器通常会向您展示如何做这些事情的最佳方法。 - Neville Nazerane
@RoughPlace,谢谢你的建议。那就是帮助了我的东西。 - SimpleUser
7个回答

529

使用Select标签帮助程序渲染SELECT元素

在您的GET操作中,创建一个视图模型对象,加载EmployeeList集合属性并将其发送到视图。

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

在您的创建视图中,从EmployeeList属性创建一个新的SelectList对象,并将其作为asp-items属性的值传递。
@model MyViewModel
<form asp-controller="Home" asp-action="Create">

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

    <input type="submit"/>

</form>

你需要编写HttpPost操作方法来接收提交的表单数据。

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

或者

如果您的视图模型具有 List<SelectListItem> 作为下拉列表项的属性。

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

在你的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);
}

在视图中,您可以直接使用Employees属性作为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>

SelectListItem属于Microsoft.AspNet.Mvc.Rendering命名空间。

确保为选择元素使用显式的闭合标签。如果使用自闭合标签方法,则标签助手将呈现一个空的SELECT元素!

以下方法不起作用

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

但这个会起作用。
<select asp-for="EmployeeId" asp-items="@Model.Employees"></select>

使用实体框架从数据库表中获取数据

上述示例使用硬编码的选项。因此,我想添加一些示例代码,使用实体框架获取数据,因为很多人使用它。

假设您的 DbContext 对象具有名为 Employees 的属性,该属性的类型为 DbSet<Employee>,其中 Employee 实体类具有此类似的 IdName 属性

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

您可以使用LINQ查询获取员工,使用Select方法在LINQ表达式中为每个员工创建一个对象列表。
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);
}

假设context是您的数据库上下文对象。视图代码与上面相同。

使用SelectList

有些人更喜欢使用SelectList类来保存呈现选项所需的项目。
public class MyViewModel
{
    public int EmployeeId { get; set; }
    public SelectList Employees { set; get; }
}

现在在您的 GET 操作中,您可以使用 SelectList 构造函数来填充视图模型的 Employees 属性。确保指定了 dataValueFielddataTextField 参数。您可以使用 nameof expression 静态链接字段名称。
public IActionResult Create()
{
    var vm = new MyViewModel();
    vm.Employees = new SelectList(GetEmployees(), nameof(Employee.Id), nameof(Employee.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" }
    };
}

我在此调用GetEmployees方法以获取一个包含每个员工对象的IdFirstName属性的列表,并将这些属性用作我们创建的SelectList对象的DataValueFieldDataTextField。您可以将硬编码列表更改为从数据库表中读取数据的代码。
视图代码将保持不变。
<select asp-for="EmployeeId" asp-items="@Model.Employees" >
    <option>Please select one</option>
</select>

从字符串列表中渲染SELECT元素。

有时,您可能想要从字符串列表中渲染选择元素。在这种情况下,您可以使用仅接受 IEnumerable<T>SelectList 构造函数。

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

视图代码将保持不变。

设置选定选项

有时,您可能希望将一个选项设置为 SELECT 元素中的默认选项(例如,在编辑屏幕上,您希望加载先前保存的选项值)。要做到这一点,您只需将 EmployeeId 属性值设置为您想要选择的选项的值即可。

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

这将在页面渲染时选择下拉列表中的Tom选项。

多选下拉列表

如果您想呈现多选下拉列表,只需将视图模型属性更改为数组类型,该属性用于视图中的asp-for属性。

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

这将呈现具有multiple属性的选择元素的HTML标记,使用户能够选择多个选项。
@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>

设置多选中的选项

与单选类似,将 EmployeeIds 属性值设置为您想要的值数组。

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

当页面被渲染时,这将选择多选元素中的Tom和Jerry选项。
使用ViewBag传递项目列表:
如果您不希望保留集合类型属性来将选项列表传递给视图,您可以使用动态的ViewBag来实现。(这不是我个人推荐的方法,因为ViewBag是动态的,您的代码容易出现未捕获的拼写错误
public IActionResult Create()
{       
    ViewBag.Employees = new List<SelectListItem>
    {
        new SelectListItem {Text = "Shyju", Value = "1"},
        new SelectListItem {Text = "Sean", Value = "2"}
    };
    return View(new MyViewModel());
}

在视图中

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

使用ViewBag传递项目列表并设置选定的选项

与上面相同。您需要做的就是将属性(为其绑定下拉列表)的值设置为所选选项的值。

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

并在视图中

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

分组项目

选择标签助手方法支持在下拉列表中对选项进行分组。您只需在操作方法中指定每个SelectListItemGroup属性值即可。

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

视图代码没有改变。选择标签助手现在会将选项呈现在2个optgroup项目内。

1
谢谢!有点像旧的剃须刀语法,我们需要执行转换为SelectList。再次感谢。 - Sam
8
我正在寻找如何添加一个空选项,感谢使用<option>请选择</option> - Serj Sagan
我在@model MyViewModel上遇到了一个错误,指出MyViewModel不存在。我复制了你的类并将其粘贴到HomeController.cs中,并在该文件中添加了你的Create()。我对此很陌生,但似乎在.net core方面我错过了什么重要的东西。我可以在几分钟内用asp.net编写所有内容,而我花费了数小时来尝试使它适用于Linux asp.net core。这开始让我感到非常恼火。 - user1853517
5
请注意,必须有一个闭合的</select>标签 - 如果您尝试像<select ... />这样进行自我关闭,则无法正常工作。另外,如果您使用空选项,例如“请选择一个”,则需要将其值设置为空字符串"",以使必填字段验证正常工作。 - Andy
3
请确保使用明确的闭合标签来闭合select元素。如果使用自闭合标签的方法,标签助手将呈现一个空的SELECT元素!这是我遇到的问题。谢谢! - Mariusz
显示剩余13条评论

15

我创建了一个接口和一个<options>标签助手。这样,每当我需要填充<select>控件时,就不必每次将IEnumerable<T>项转换为IEnumerable<SelectListItem>

我认为它工作得很好...

用法类似于:

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

为了让标签助手起作用,您必须在类中实现该接口:

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

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

以下是所需的代码:

界面:

public interface IIntegerListItem
{
    int Value { get; }
    string Text { get; }
}
< p > <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>");
            }
        }
    }
}

可能会有一些错别字,但我认为目的很明确。我不得不稍微编辑一下。


6

以下是我的答案,不解决问题,但与问题相关。

如果有人使用 enum 而不是类模型,就像这个例子:

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

还有一个属性可以在提交时获取值:

public int No { get; set; }

在 Razor 页面中,您可以使用 Html.GetEnumSelectList<Counter>() 方法来获取枚举属性。
<select asp-for="No" asp-items="@Html.GetEnumSelectList<Counter>()"></select>

它会生成以下的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>

4

您还可以使用 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;

3
您可以使用下面的代码实现多选功能:
<select asp-for="EmployeeId"  multiple="multiple" asp-items="@ViewBag.Employees">
    <option>Please select</option>
</select>

你也可以使用:

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

0

asp-items 需要一些 SelectListItem 类的实例集合(来自 Microsoft.AspNetCore.Mvc.Rendering 命名空间),其中包含 TextValue 属性。因此,您可以通过这种方式将您的 Employees 集合转换为所需的集合,例如:employees.Select(e => new SelectListItem(e.FullName, e.Id.ToString())) 并将结果放入 asp-items 属性中。


0

在获取中:

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

在发布中:
var selectedIds= Request.Form["Tags"];

在视图中:
<label>Tags</label>
<select  asp-for="Tags"  id="Tags" name="Tags" class="form-control" asp-items="ViewBag.Tags" multiple></select>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接