【问题标题】:How do I implement a checkbox list in ASP.NET Core?如何在 ASP.NET Core 中实现复选框列表?
【发布时间】:2017-03-26 03:07:57
【问题描述】:

我希望在 ASP.NET Core 中实现一个复选框列表,但遇到了一些困难。

我的视图模型:

public class GroupIndexViewModel
{
    public Filter[] Filters { get; set; }
}

public class Filter
{
    public int Id { get; set; }
    public string Name { get; set; }
    public bool Selected { get; set; }
}

我的观点:

@model GroupIndexViewModel
<form asp-action="Index" asp-controller="Group" method="get">
  <ul>
  @for (var i = 0; i < Model.Filters.Length; i++)
  {
    <li>
      <input type="checkbox" id="@Model.Filters[i].Name" asp-for="@Model.Filters[i].Selected" value="@Model.Filters[i].Selected" checked="@Model.Filters[i].Selected" />
      <label for="@Model.Filters[i].Name">@Model.Filters[i].Name</label>
    </li>
  }
  </ul>
  <button type="submit" name="action">Filtrer</button>
</form>

当发布到我的控制器时,我的视图模型中的 Filter 属性显示 selected false 即使它在视图中被选中。

【问题讨论】:

  • 你试过用Html.CheckboxFor( m =&gt; m.Filters[i].Name )代替吗?
  • 另外,使用现有 Razor 代码呈现的 HTML 是什么样的?
  • 如果除了 asp-for 属性之外还为复选框输入字段指定了选中和/或值属性,则实际复选框状态将不会正确传递给 POST 操作。如果您只使用 asp-for 将其排除在外,那么它将起作用。

标签: c# asp.net-core


【解决方案1】:

我会按照以下方式进行。

@model GroupIndexViewModel
<form asp-action="Index" asp-controller="Group" method="get">
    <ul>
        @for (var i = 0; i < Model.Filters.Count; i++)
        {
            <li>       
                <input type="checkbox" asp-for="@Model.Filters[i].Selected"  />
                <label asp-for="@Model.Filters[i].Selected">@Model.Filters[i].Name</label>
                <input type="hidden" asp-for="@Model.Filters[i].Id" />
                <input type="hidden" asp-for="@Model.Filters[i].Name" />                
            </li>
        }
    </ul>
    <button type="submit" name="action">Filtrer</button>
</form>

这里我假设您已经正确实现了控制器和操作。

【讨论】:

  • 没有。就像我在复选框中保留整个模型以及值更改所做的那样。
  • 如果您查看 Filter 类,它具有三个属性 Id、Name 和 Selected。名称用于显示对复选框。否则复选框不会显示任何内容。我们也可以不使用标签直接打印@Model.Filters[i].Name,但问题是我使用了该标签,因为我没有看到任何问题。
  • 这里的关键是 asp-for 仅从 标签添加到传回的 ViewModel。这就是为什么我们需要存储 Model..Name 的隐藏文件,它也被无关地用于提供
  • 另一个重要提示 - 您必须使用“for”而不是“foreach”,以便视图模型可以拾取属性名称(在本例中为“Filter”)
【解决方案2】:

这个问题可能已经回答了,但我想解释一下你的问题,以便其他人可以理解发生了什么。

您不知道您已经在输入中指定了 false 值,因为您正在实现对属性的错误使用。

我们来看看你的看法

@model GroupIndexViewModel
<form asp-action="Index" asp-controller="Group" method="get">
   <ul>
     @for (var i = 0; i < Model.Filters.Length; i++)
     {
      <li>
        <input type="checkbox" id="@Model.Filters[i].Name" asp-for="@Model.Filters[i].Selected" value="@Model.Filters[i].Selected" checked="@Model.Filters[i].Selected" />
        <label for="@Model.Filters[i].Name">@Model.Filters[i].Name</label>
     </li>
     }
  </ul>
  <button type="submit" name="action">Filtrer</button>
</form>

所以,首先。您正在从Filter 的数组创建输入元素。现在让我们仔细看看您的输入元素。

<input type="checkbox" id="@Model.Filters[i].Name" asp-for="@Model.Filters[i].Selected" value="@Model.Filters[i].Selected" checked="@Model.Filters[i].Selected" />

现在,让我解释一下。

  1. 您正在使用type 属性指定类型。
  2. 您正在使用id 属性指定一个ID。
  3. 您使用asp-for 标签助手将输入绑定到模型。
  4. 您使用value 属性为输入指定一个值。
  5. 最后,使用checked 属性将输入设置为选中状态。

如果你看一下Tag Helpers Documentation,你会发现.Net TypeInput Type之间的关系,理解为:

Checkboxes 包含一个 boolean 值,对应于模型,并由标签助手格式化为 type="checkbox"

由于您使用的是 type="checkbox" 属性,因此 Tag Helper 值只能是 truefalse。因此,如果我们返回并查看输入元素,您已经为输入指定了一个值。即使标签助手可以为输入分配一个值,它也不能覆盖已经指定的值。因此,您的输入将始终具有您指定的值,在这种情况下,boolean始终为false by default

现在您可能认为您的输入元素具有false 值,例如,添加checked="checked" 不会将值更改为true,因为value属性,覆盖checked 属性。导致两个属性的错误实现。

因此,您必须只使用一个属性。 (valuechecked)。为了方便起见,您可以使用它们。但在这种情况下,您必须使用默认的checked 属性。由于您正在实现标签助手,并且输入值必须是boolean 类型。而checked 属性值,例如returns a boolean,就是标签助手使用的属性值。

所以@dotnetstep 提供的实现应该可以工作,因为它只在输入元素中声明了标签助手。所以 Tag Helper 自己处理输入的相应属性。

@model GroupIndexViewModel
<form asp-action="Index" asp-controller="Group" method="get">
    <ul>
        @for (var i = 0; i < Model.Filters.Count; i++)
        {
            <li>       
                <input type="checkbox" asp-for="@Model.Filters[i].Selected"  />
                <label asp-for="@Model.Filters[i].Selected">@Model.Filters[i].Name</label>
                <input type="hidden" asp-for="@Model.Filters[i].Id" />
                <input type="hidden" asp-for="@Model.Filters[i].Name" />                
            </li>
        }
    </ul>
    <button type="submit" name="action">Filtrer</button>
</form>

【讨论】:

    【解决方案3】:

    基于@dotnetstep 的回答,我创建了一个Tag Helper,它采用IEnumerable of SelectListItem 的模型并生成他的回答中描述的字段。

    这里是标签助手代码:

    [HtmlTargetElement(Attributes = "asp-checklistbox, asp-modelname")]
    public class CheckListBoxTagHelper : TagHelper
    {
        [HtmlAttributeName("asp-checklistbox")]
        public IEnumerable<SelectListItem> Items { get; set; }
    
        [HtmlAttributeName("asp-modelname")]
        public string ModelName { get; set; }
    
        public override void Process(TagHelperContext context, TagHelperOutput output)
        {
            var i = 0;
            foreach (var item in Items)
            {
                var selected = item.Selected ? @"checked=""checked""" : "";
                var disabled = item.Disabled ? @"disabled=""disabled""" : "";
    
                var html = $@"<label><input type=""checkbox"" {selected} {disabled} id=""{ModelName}_{i}__Selected"" name=""{ModelName}[{i}].Selected"" value=""true"" /> {item.Text}</label>";
                html += $@"<input type=""hidden"" id=""{ModelName}_{i}__Value"" name=""{ModelName}[{i}].Value"" value=""{item.Value}"">";
                html += $@"<input type=""hidden"" id=""{ModelName}_{i}__Text"" name=""{ModelName}[{i}].Text"" value=""{item.Text}"">";
    
                output.Content.AppendHtml(html);
    
                i++;
            }
    
            output.Attributes.SetAttribute("class", "th-chklstbx");
        }
    }
    

    您需要将以下内容添加到 _ViewImports.cshtml 文件中:

    @addTagHelper *, <ProjectName>
    

    然后将检查列表框拖放到您的 razor 视图中,就像这样简单:

    <div asp-checklistbox="Model.Brands" asp-modelname="Brands"></div>
    

    您可能会注意到我正在向 div 添加一个类属性来设置框及其内容的样式。这是 CSS:

    .th-chklstbx {
      border: 1px solid #ccc;
      padding: 10px 15px;
      -webkit-border-radius: 5px ;
      -moz-border-radius: 5px ;
      -ms-border-radius: 5px ;
      border-radius: 5px ; 
    }
    .th-chklstbx label {
        display: block;
        margin-bottom: 10px; 
    }
    

    【讨论】:

    • 这很棒。不确定 .Net Core 1 但在 .Net Core 2 上不要忘记将其添加到您的 __ViewImports.cshtml 文件中:@addTagHelper *,
    • 感谢您提供更多信息敏捷迈克!我忘了包括那个要求。我已将其添加到我的回复中。
    【解决方案4】:

    站在@dotnetstep 和@gsxrboy73 的肩膀上,这种方法添加了一个可选的控件标题和“全选”类型的复选框。它还序列化“id”属性,因此您可以安全地在页面上拥有多个复选框列表。这是为 .NET 5 绑定到 Bootstrap 环境中的 MVC 模型量身定制的。

    我更喜欢不包含在巨型 mvc 库中的轻薄模型:

    public class CheckBoxListItem
    {
        public string Key { get; set; }
        public string Value { get; set; }
        public bool IsChecked { get; set; } = false;
        public bool IsDisabled { get; set; } = false;
    }
    

    List&lt;CheckBoxListItem&gt; 驱动 Tag Helper:

    /// <summary>check-box-list Tag Helper</summary>
    [HtmlTargetElement("Check-Box-List", Attributes = "asp-title, asp-items, asp-model-name, asp-check-all-label", TagStructure=TagStructure.NormalOrSelfClosing)]
    public class CheckBoxListTagHelper : TagHelper
    {
        /// <summary>HTML element ID of the tracking form element</summary>
        [HtmlAttributeName("asp-form-id")]
        public string FormId { get; set; }
    
        /// <summary>Optional bolder title set above the check box list</summary>
        [HtmlAttributeName("asp-title")]
        public string ListTitle { get; set; }
    
        /// <summary>List of individual child/item values to be rendered as check boxes</summary>
        [HtmlAttributeName("asp-items")]
        public List<CheckBoxListItem> Items { get; set; }
    
        /// <summary>The name of the view model which is used for rendering html "id" and "name" attributes of each check box input.
        /// Typically the name of a List[CheckBoxListItem] property on the actual passed in @Model</summary>
        [HtmlAttributeName("asp-model-name")]
        public string ModelName { get; set; }
    
        /// <summary>Optional label of a "Check All" type checkbox.  If left empty, a "Check All" check box will not be rendered.</summary>
        [HtmlAttributeName("asp-check-all-label")]
        public string CheckAllLabel { get; set; }
    
        public override void Process(TagHelperContext context, TagHelperOutput output)
        {
            string id = context.UniqueId;
            output.TagName = "div";
            string html = "";
    
            output.PreElement.AppendHtml($"<!-- Check List Box for {(string.IsNullOrEmpty(ListTitle) ? ModelName : ListTitle)} -->\r\n");
    
            if (!string.IsNullOrEmpty(ListTitle))
            {
                // Prepend a Title to the control
                output.PreContent.AppendHtml($"\r\n\t<label id=\"check-box-list-label-{id}\" class=\"cblTitle\">\r\n"
                    + $"\t\t<strong>{ListTitle}</strong>\r\n"
                    + $"\t</label>\r\n");
            }
    
            if (!string.IsNullOrEmpty(CheckAllLabel))
            {
                // Prepend a "Check All" type checkbox to the control
                output.PreContent.AppendHtml("\t<div class=\"form-check\">\r\n"
                    + $"\t\t<input id=\"check-box-list-all-{id}\"\r\n"
                    + "\t\t\tclass=\"cblCheckAllInput form-check-input\"\r\n"
                    + "\t\t\ttype=\"checkbox\"\r\n"
                    + $"\t\t\tvalue=\"true\"\r\n"
                    );
    
                if (Items.All(cbli => cbli.IsChecked))
                {
                    output.PreContent.AppendHtml("\t\t\tchecked=\"checked\"\r\n");
                }
    
                output.PreContent.AppendHtml("\t\t\t/>\r\n"
                    + $"\t\t<label id=\"check-box-list-all-label-{id}\" class=\"cblCheckAllLabel form-check-label\" for=\"check-box-list-all-{id}\">\r\n"
                    + $"\t\t\t&nbsp; {CheckAllLabel}\r\n"
                    + "\t\t</label>\r\n"
                    + "\t</div>\r\n"
                    ) ;
            }
    
            // Begin the actual Check Box List control
            output.Content.AppendHtml($"\t<div id=\"cblContent-{id}\" class=\"cblContent\">\r\n");
    
            // Create an individual check box for each item
            for (int i = 0; i < Items.Count(); i++)
            {
                CheckBoxListItem item = Items[i];
                html = "\t\t<div class=\"form-check\">\r\n"
                    + $"\t\t\t<input id=\"{ModelName}_{i}__IsChecked-{id}\"\r\n"
                    + $"\t\t\t\tname=\"{ModelName}[{i}].IsChecked\"\r\n"
                    + $"\t\t\t\tclass=\"cblCheckBox form-check-input\"\r\n"
                    + $"\t\t\t\tform=\"{FormId}\"\r\n"
                    + "\t\t\t\tdata-val=\"true\"\r\n"
                    + "\t\t\t\ttype=\"checkbox\""
                    + "\t\t\t\tvalue=\"true\""
                    ;
    
                if (item.IsChecked)
                {
                    html += "\t\t\t\tchecked=\"checked\"\r\n";
                }
    
                if (item.IsDisabled)
                {
                    html += "\t\t\t\tdisabled=\"disabled\"\r\n";
                }
    
                html += "\t\t\t\t/>\r\n"
                    + $"\t\t\t<label id=\"check-box-list-item-label-{id}-{i}\" class=\"cblItemLabel form-check-label\" for=\"{ModelName}_{i}__IsChecked-{id}\">\r\n"
                    + $"\t\t\t\t&nbsp; {item.Value}\r\n"
                    + "\t\t\t</label>\r\n"
                    + $"\t\t\t<input type=\"hidden\" id=\"{ModelName}_{i}__IsChecked-{id}-tag\" name=\"{ModelName}[{i}].IsChecked\" form =\"{FormId}\" value=\"false\">\r\n"
                    + $"\t\t\t<input type=\"hidden\" id=\"{ModelName}_{i}__Key-{id}\" name=\"{ModelName}[{i}].Key\" form =\"{FormId}\" value=\"{item.Key}\">\r\n"
                    + $"\t\t\t<input type=\"hidden\" id=\"{ModelName}_{i}__Value-{id}\" name=\"{ModelName}[{i}].Value\" form =\"{FormId}\" value=\"{item.Value}\">\r\n"
                    + "\t\t</div>\r\n"
                    ;
    
                output.Content.AppendHtml(html);
            }
    
            output.Content.AppendHtml("\t</div>\r\n");
            output.Attributes.SetAttribute("id", $"check-box-list-{id}");
            output.Attributes.SetAttribute("class", "cblCheckBoxList");
        }
    }
    

    揭示原型 JS 使“检查所有”框与其他框相得益彰:

    // Attach event handlers to controls
    $(function () {
    
        // Toggle child check boxes per the "Check All" check box state
        $("div.cblCheckBoxList").on("click", "input.cblCheckAllInput", function (event) {
    
            let chkBoxListDiv = $(event.target).closest("div.cblCheckBoxList");
            let chkBoxList = new checkBoxList($(chkBoxListDiv).attr("id"), $(event.target).attr("id"));
    
            chkBoxList.onCheckAllClick();
        });
    
        // Sync the "Check All" box w/ the child check boxes' check box states
        $("div.cblCheckBoxList").on("click", "input.cblCheckBox", function (event) {
    
            let chkBoxListDiv = $(event.target).closest("div.cblCheckBoxList");
            let chkBoxList = new checkBoxList($(chkBoxListDiv).attr("id"), null);
    
            chkBoxList.onCheckItemClick();
        });
    });
    
    
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //
    //  Check Box List
    //
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    
    var checkBoxList = function (checkBoxListDivId, checkAllInputId) {
    
        this.listDivId = checkBoxListDivId;
        this.allInputId = (checkAllInputId ?? $("#" + this.listDivId).find("input.cblCheckAllInput")?.first()?.attr("id"));
    };
    
    checkBoxList.prototype = function () {
    
        // If a "Check All" type check box is clicked, update the individual child check boxes accordingly
        var onCheckAllClick = function () {
    
            // Find the "Check All" check box that was clicked
            let checkAllInput = $('#' + this.allInputId);
    
            // Determine whether the "Check All" check box is checked or unchecked
            let chkd = $(checkAllInput).prop('checked');
    
            // Get a list of child/item check boxes
            let chks = $('#' + this.listDivId).find('input.cblCheckBox');
    
            // Make the child/item check boxes match the value of the "Check All" check box
            chks.prop('checked', chkd);
        },
    
        // If an individual child check box is clicked and a "Check All" type checkbox exists, update it accordingly
        onCheckItemClick = function () {
    
            if (!((this.allInputId === undefined) || (this.allInputId.length === 0))) {
    
                // Get an array of check boxes that are NOT checked
                let notChkd = $('#' + this.listDivId).find("input.cblCheckBox:not(:checked)");
    
                // Update the "Check All" check box accordingly
                $("#" + this.allInputId).prop('checked', (notChkd.length === 0));
            }
        };
    
        return {
            onCheckAllClick: onCheckAllClick,
            onCheckItemClick: onCheckItemClick
        };
    }();
    

    涂上一些 CSS 口红:

    /*  For CheckBoxList form control   */
    .cblCheckBoxList {
        border: 1px solid #ccc;
        padding: 10px 15px;
        margin-right: 10px;
        -webkit-border-radius: 5px;
        -moz-border-radius: 5px;
        -ms-border-radius: 5px;
        border-radius: 5px;
    }
        .cblCheckBoxList .cblContent {
            height: 150px;
            overflow-y: scroll;
            padding: 0;
            margin: 0;
        }
    
        .cblCheckBoxList .cblTitle {
            font-weight: bolder;
        }
    
        .cblCheckBoxList .cblCheckAllLabel {
            margin-bottom: 10px;
        }
    
        .cblCheckBoxList .cblCheckAllInput {
            margin-bottom: 0;
        }
    
        .cblCheckBoxList .cblItemLabel {
            margin-bottom: 0;
            font-size: small;
        }
    
        .cblCheckBoxList .cblCheckBox {
            margin-bottom: 0;
            font-size: small;
        }
    

    然后将复选框列表拖放到页面上:

    @* On the passed in View Model, "IceCreamFlavors" is a property that is a List of type CheckBoxListItem *@
    <check-box-list 
        asp-title="Ice Cream Flavors"
        asp-items="Model.IceCreamFlavors"
        asp-model-name="IceCreamFlavors"
        asp-form-id="my-form-id"
        asp-check-all-label="All Flavors"
        >
    </check-box-list>
    

    博塔繁荣,博塔兵。

    【讨论】:

    • 在我遇到的最奇怪的怪癖之一中,无论我做了什么,列表中的第一个复选框在将提交的数据绑定到操作的视图时,永远不会向控制器操作传达检查状态模型参数。我发现的唯一解决方法是将 form="my-form-id' 属性添加到呈现的复选框输入中。
    • 它不会在发布操作中回发给模型。你是如何让它发挥作用的?
    【解决方案5】:

    我刚试过,效果很好:

    <input asp-for="filter.type[i].IsEnabled"/>
    

    不带复选框,然后是模型中对应的布尔值,隐藏 id 和名称。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-05-30
      • 2020-02-03
      • 2023-02-16
      • 2017-12-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多