【问题标题】:Select Tag Helper in ASP.NET Core MVC在 ASP.NET Core MVC 中选择 Tag Helper
【发布时间】:2016-04-10 00:09:41
【问题描述】:

我需要一些有关 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="???" />

我很感激这方面的帮助。谢谢。

【问题讨论】:

  • 只是我认为我应该添加的东西,如果您立即关闭选择标签,它似乎不起作用总是用关闭标签标签助手不适用于
  • 只是一个提示。脚手架控制器通常会向您展示此类事情的最佳方法
  • @RoughPlace,感谢您的提示。这就是帮助我的原因

标签: c# asp.net-core asp.net-core-mvc tag-helpers


【解决方案1】:

使用 Select Tag 助手渲染 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 action 方法接受提交的表单数据。

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

或者

如果您的视图模型具有 List&lt;SelectListItem&gt; 作为下拉项的属性。

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&lt;Employee&gt;,其中 Employee 实体类有一个 IdName 这样的属性

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

您可以使用 LINQ 查询来获取员工,并使用 LINQ 表达式中的 Select 方法为每个员工创建 SelectListItem 对象列表。

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 类来保存渲染选项所需的项目。

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 方法来获取Employee 对象的列表,每个对象都有一个IdFirstName 属性,我将这些属性用作DataValueFieldDataTextField 对象的DataTextField我们创造了。您可以将硬编码列表更改为从数据库表中读取数据的代码。

视图代码将相同。

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

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

有时您可能希望从字符串列表中呈现选择元素。在这种情况下,您可以使用 SelectList 构造函数,它只接受 IEnumerable&lt;T&gt;

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

这将在呈现页面时选择 select 元素中的选项 Tom。

多选下拉菜单

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

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

这将呈现带有multiple 属性的select 元素的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 and 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 项内的选项。

【讨论】:

  • 谢谢!有点像旧的剃刀语法,我们需要执行到 SelectList 的转换。再次感谢。
  • 不客气!如果您在视图模型中使用List&lt;SelectListItem&gt; 作为属性,则无需在视图中对其进行转换。在答案中查看我的更新。
  • 我正在寻找如何添加一个空选项,感谢您使用&lt;option&gt;Please select one&lt;/option&gt;
  • 请注意,您必须有一个结束 标记 - 如果您尝试像 这样自行关闭 select 标记,它将不起作用。此外,如果您使用“请选择一个”之类的空选项,则需要为其指定一个空字符串“”值,以使必填字段验证生效。
  • "确保您为 select 元素使用显式关闭标签。如果您使用自关闭标签方法,标签助手将呈现一个空的 SELECT 元素!"这就是我的问题。干杯!
【解决方案2】:

我为此创建了一个接口和一个&lt;options&gt; 标签助手。所以我不必每次必须填充&lt;select&gt; 控件时都将IEnumerable&lt;T&gt; 项目转换为IEnumerable&lt;SelectListItem&gt;

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

用法是这样的:

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

&lt;options&gt; 标签助手:

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

可能有一些错字,但我认为目的很明确。我不得不稍微修改一下。

【讨论】:

    【解决方案3】:

    我在下面的回答没有解决问题,但它有关。

    如果有人使用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; }
    

    在剃须刀页面中,可以使用Html.GetEnumSelectList&lt;Counter&gt;()获取枚举属性。

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

      【讨论】:

        【解决方案5】:

        您可以使用以下代码进行多选

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

        【讨论】:

          【解决方案6】:

          在获取中:

          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>
          

          【讨论】:

            【解决方案7】:

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

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2017-11-16
              • 2018-11-03
              • 1970-01-01
              • 2017-07-05
              • 2017-11-17
              • 2018-02-13
              相关资源
              最近更新 更多