C# 如何在ASP.NET Core中实现复选框列表?
我希望在ASP.NET核心中实现一个复选框列表,但我面临一些困难 我的ViewModel:C# 如何在ASP.NET Core中实现复选框列表?,c#,asp.net-core,C#,Asp.net Core,我希望在ASP.NET核心中实现一个复选框列表,但我面临一些困难 我的ViewModel: 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; s
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>
@model GroupIndexViewModel
@对于(var i=0;i
@Model.Filters[i].Name
}
过滤器
当发布到我的控制器时,我的viewmodel中的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>
@model GroupIndexViewModel
@对于(var i=0;i
@Model.Filters[i].Name
}
过滤器
在这里,我假设您已经正确地实施了控制器和操作。这个问题可能已经得到了回答,但我想解释一下您的问题,以便其他人能够理解发生了什么。
您不知道您已经在为输入指定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>
现在,让我解释一下
type
属性指定类型id
属性指定一个idasp for
标记帮助器将输入绑定到模型value
属性为输入指定一个值checked
属性将输入设置为选中type=“checkbox”
由于您使用的是type=“checkbox”
属性,标记帮助器值只能是true
或false
。因此,如果我们返回并查看输入元素,您已经为输入指定了一个值。即使标记帮助器可以为输入指定一个值,它也不能覆盖已经指定的值。因此,您的输入将始终具有您指定的值,在本例中,布尔值
是始终
现在,您可能认为您的输入元素有一个false
值,例如,添加checked=“checked”
不会将值更改为true
,因为值
属性会覆盖checked
属性。导致两个属性的错误实现
因此,您必须只使用一个属性。(要么是值
,要么是选中的)。为了方便,你可以使用它们。但是在这种情况下,必须使用默认的checked
属性。因为您要实现标记帮助器,所以输入值必须是boolean
类型。而选中的
属性值,例如,是标记帮助器使用的值
因此,提供的实现应该可以工作,因为它只在输入元素中声明标记帮助器。因此,标记帮助器自行处理输入的相应属性
@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>
@model GroupIndexViewModel
@对于(var i=0;i
@Model.Filters[i].Name
}
过滤器
基于@dotnetstep的答案,我创建了一个标记帮助器,它采用SelectListItem的IEnumerable模型,并生成他答案中描述的字段
以下是标记帮助器代码:
[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");
}
}
我刚试过这个,效果很好:
<input asp-for="filter.type[i].IsEnabled"/>
没有复选框,然后在模型中使用隐藏id和名称对应的布尔值。站在@dotnetstep和@gsxrboy73的肩膀上,这种方法添加了可选的控件标题和“全部检查”类型的复选框。它还序列化“id”属性,这样您就可以在一个页面上安全地拥有多个复选框列表。这是为.NET5绑定到自举环境中的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;
}
列表
驱动标记帮助器:
/// <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 {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 {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");
}
}
贴上一些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>
@*在传入的视图模型上,“IceCreamFlavors”是一个CheckBoxListItem类型列表的属性*@
Botta boom,Botta bing。您是否尝试过使用Html.CheckboxFor(m=>m.Filters[i].Name)
替代?另外,您现有的Razor代码呈现的Html是什么样子的?如果除了asp for属性之外,您还为复选框输入字段指定了checked和/或value属性,那么实际的复选框状态将不会正确地传递给POST操作。如果你不使用asp,那么它就可以工作了。正如我在复选框中保存整个模型以及值更改时所做的那样。如果您查看筛选器类,它有三个属性Id、名称和选定项。名称用于对照复选框显示。否则,复选框将不显示任何内容。我们也可以不使用标签直接打印@Model.Filters[i].Name,但问题是我使用了这个标签,因为我看不出有任何问题。这里的关键是asp,用于仅从标记添加到传递回的ViewModel。这就是为什么我们需要存储Model..Name的隐藏项,它也未被最近用于提供文本。我花了一段时间才弄明白这一点,这相当令人沮丧…另一个关键注意事项-您必须使用“for”而不是“foreach”,以便viewmodel可以在属性名称上显示(在本例中)
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;
}
/// <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 {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 {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");
}
}
// 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
};
}();
/* 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>