【发布时间】:2011-05-16 01:03:55
【问题描述】:
我刚刚开始学习 ASP.NET MVC。我将如何创建一个可重复使用的三态复选框?在 WebForms 中,这将是一个控件,但我不知道 MVC 等效项。
【问题讨论】:
标签: asp.net-mvc
我刚刚开始学习 ASP.NET MVC。我将如何创建一个可重复使用的三态复选框?在 WebForms 中,这将是一个控件,但我不知道 MVC 等效项。
【问题讨论】:
标签: asp.net-mvc
将TriStateCheckBox(或TriStateCheckBoxFor,如果您使用强类型重载)扩展方法添加到HtmlHelper,并将该扩展方法类的命名空间添加到您的web.config 的namespaces 部分。
至于实现,我建议查看 the InputExtensions source on codeplex 并使用它来创建自己的。
【讨论】:
查看渲染 - 渲染 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 @987654325@, but duplicated to remove dependency on Forms.dll</remarks>
public enum CheckState
{
Checked,
Indeterminate,
Unchecked
}
然后将以下属性添加到您的模型中而不是布尔值:
public CheckState OpenTasks { get; set; }
然后为属性创建一个 EditorTemplate,该属性将呈现我们想要在隐藏输入中持久化的实际属性PLUS一个复选框控件,我们将使用它来更新那个属性
Views/Shared/EditorTemplates/<b>CheckState.cshtml</b>:
@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 相同的 hack 来提交两个具有相同名称的字段,并首先放置我们想要保留的 HiddenFor 值,以便它获胜。这只是为了方便遍历 DOM 并找到相应的值,但您可以使用不同的名称来防止任何可能的重叠。
然后,在您看来,您可以使用编辑器模板渲染属性和复选框,就像使用复选框一样,因为它同时渲染两者。因此,只需将其添加到您的视图中:
@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? 中的 getter/setter 公开/修改,如下所示:
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
【讨论】:
ASP.NET MVC 当然不提供这样的组件,实际上它只是依赖于 HTML 中可用的标准元素,但你可能想看看这个solution。
【讨论】: