限制:
视图渲染
-当呈现HTML内容时,没有属性可以放在
<input type="checkbox" />
那会给它财产
indeterminate
.
总有一天,你会
必须使用JavaScript
抓住元素
set the indeterminate property
:
// vanilla js
document.getElementById("myChk").indeterminate = true;
// jQuery
$("#myCheck).prop("indeterminate", true);
表单数据
-模型绑定将始终限制为请求中实际发送的值,无论是从url还是数据负载(在POST上)。
在这个简化的示例中,未选中和不确定复选框的处理方式相同:
你可以在这里亲自确认
堆栈段
:
label {
display: block;
margin-bottom: 3px;
}
<form action="#" method="post">
<label >
<input type="checkbox" name="chkEmpty">
Checkbox
</label>
<label >
<input type="checkbox" name="chkChecked" checked>
Checkbox with Checked
</label>
<label >
<input type="checkbox" name="chkIndeterminate" id="chkIndeterminate">
<script> document.getElementById("chkIndeterminate").indeterminate = true; </script>
Checkbox with Indeterminate
</label>
<label >
<input name="RegularBool" type="checkbox" value="true">
<input name="RegularBool" type="hidden" value="false">
RegularBool
</label>
<input type="submit" value="submit"/>
</form>
模型绑定
-此外,模型绑定将只发生在实际发送的属性上。这实际上给常规复选框带来了问题,因为它们在未选中时不会发布值。值类型总是有一个默认值,但是,如果这是模型中的唯一属性,那么如果MVC没有看到任何属性,它就不会更新整个类。
ASP.NET通过每个复选框发出两个输入来解决此问题:
注意
:隐藏的输入保证即使复选框不是
checked
. 当复选框
已检查
,允许HTTP提交具有相同名称的多个值,但ASP.NET MVC将只接受第一个实例,因此它将返回
true
就像我们预料的那样。
仅渲染解决方案
我们可以
checkbox for a nullable boolean
,但是,这实际上只能通过转换
null
â
false
渲染时。在服务器和客户端之间共享不确定状态仍然很困难。如果您不需要发回不确定,这可能是最干净/最简单的实现。
往返解决方案
由于使用HTML复选框捕获和发布所有3个可见状态有严重的限制,让我们将控件视图(复选框)与要持久化的三态值分开,然后通过JavsScript保持它们同步。既然我们已经需要JS了,这并没有真正增加我们的依赖链。
从保存我们的值的枚举开始:
/// <summary> Specifies the state of a control, such as a check box, that can be checked, unchecked, or set to an indeterminate state.</summary>
/// <remarks> Adapted from System.Windows.Forms.CheckState, but duplicated to remove dependency on Forms.dll</remarks>
public enum CheckState
{
Checked,
Indeterminate,
Unchecked
}
然后将以下属性添加到模型中
而不是
布尔值:
public CheckState OpenTasks { get; set; }
然后创建
编辑模板
对于将呈现要在隐藏输入内持久化的实际属性的属性
加
用于更新该属性的复选框控件
Views/Shared/EditorTemplates/
CheckState.cshtml
:
@model CheckState
@Html.HiddenFor(model => model, new { @class = "tri-state-hidden" })
@Html.CheckBox(name: "",
isChecked: (Model == CheckState.Checked),
htmlAttributes: new { @class = "tri-state-box" })
注意
:我们使用与ASP.NET MVC相同的黑客来提交两个同名字段,并将
HiddenFor
我们首先要坚持的价值观,这样它才会赢。这使得遍历DOM并找到相应的值变得很容易,但是您可以使用不同的名称来防止任何可能的重叠。
然后,在视图中,可以使用编辑器模板以与使用复选框相同的方式呈现property+复选框,因为它同时呈现这两个复选框。因此,只需将此添加到您的视图中:
@Html.EditorFor(model => model.OpenTasks)
最后一点是在加载时通过JavaScript保持它们的同步,并且每当复选框像这样更改时:
// on load, set indeterminate
$(".tri-state-hidden").each(function() {
var isIndeterminate = this.value === "@CheckState.Indeterminate";
if (isIndeterminate) {
var $box = $(".tri-state-box[name='" + this.name + "'][type='checkbox']");
$box.prop("indeterminate", true);
}
});
// on change, keep synchronized
$(".tri-state-box").change(function () {
var newValue = this.indeterminate ? "@CheckState.Indeterminate"
: this.checked ? "@CheckState.Checked"
: "@CheckState.Unchecked";
var $hidden = $(".tri-state-hidden[name='" + this.name + "'][type='hidden']");
$hidden.val(newValue);
});
然后你可以在你的商业模式中随心所欲地使用。例如,如果要映射到可为空的布尔值,可以使用
CheckState
属性作为备份值,并通过
bool?
这样地:
public bool? OpenTasksBool
{
get
{
if (OpenTasks == CheckState.Indeterminate) return null;
return OpenTasks == CheckState.Checked;
}
set
{
switch (value)
{
case null: OpenTasks = CheckState.Indeterminate; break;
case true: OpenTasks = CheckState.Checked; break;
case false: OpenTasks = CheckState.Unchecked; break;
}
}
}
替代解决方案
另外,根据您的域模型,您可以使用
Yes, No, â¿/â
radio buttons