繁体   English   中英

如何在 ASP.NET Core 中实现复选框列表?

[英]How do I implement a checkbox list in ASP.NET Core?

我希望在 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 ,即使它在视图中被选中。

我会按照以下方式进行。

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

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

问题可能已经得到解答,但我想解释您的问题,以便其他人可以了解发生了什么。

您不知道您已经为输入指定了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属性指定type
  2. 您正在使用id属性指定一个 id。
  3. 您可以使用asp-for标签助手将输入绑定到模型。
  4. 您可以使用value属性为输入指定一个值。
  5. 最后,使用checked属性将输入设置为checked。

如果您查看Tag Helpers Documentation ,您会发现.Net TypeInput Type之间的关系,了解:

Checkboxes 保存一个布尔值,对应于模型,并由标签助手格式化为type="checkbox"

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

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

因此,您必须使用其中一个属性。 value或已checked )。 为方便起见,您可以使用它们。 但在这种情况下,您必须使用默认的checked属性。 由于您正在实现 Tag Helper,因此输入值必须是boolean类型。 checked属性值返回一个布尔值,例如,是标签助手使用的值。

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

基于@dotnetstep 的回答,我创建了一个标签助手,它采用SelectListItemIEnumerable模型并生成他的回答中描述的字段。

这是标签助手代码:

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

然后将检查列表框放入您的剃刀视图中,就像这样简单:

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

您可能会注意到,我正在向 div 添加一个 class 属性来设置框及其内容的样式。 这是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; 
}

站在@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<CheckBoxListItem>驱动标签助手:

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

博塔热潮,博塔宾。

我刚试过这个,它奏效了:

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

没有复选框,然后是模型中具有隐藏 ID 和名称的相应布尔值。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM