代码之家  ›  专栏  ›  技术社区  ›  Jonathan Allen

ASP.NET MVC:三态复选框

  •  4
  • Jonathan Allen  · 技术社区  · 14 年前

    我刚刚开始学习ASP.NET MVC。我该如何着手创建一个可重用的三态checbox?在WebForms中,这是一个控件,但我不知道与MVC等价的控件。

    3 回复  |  直到 14 年前
        1
  •  6
  •   Nathan Phillips    7 年前

    添加 TriStateCheckBox (或 TriStateCheckBoxFor 如果使用强类型重载)扩展方法 HtmlHelper 并将该扩展方法类的命名空间添加到 namespaces web.config的部分。

    至于实现,我建议您看看 the InputExtensions source on codeplex 用它来创造你自己的。

        2
  •  1
  •   Denis Ivin    14 年前

    NET MVC当然不提供这样的组件,实际上它仅仅依赖于HTML中可用的标准元素,但是您可能想查看一下 solution .

        3
  •  1
  •   KyleMit Steven Vachon    6 年前

    限制:

    视图渲染 -当呈现HTML内容时,没有属性可以放在 <input type="checkbox" /> 那会给它财产 indeterminate .

    总有一天,你会 必须使用JavaScript 抓住元素 set the indeterminate property :

    // vanilla js
    document.getElementById("myChk").indeterminate = true;
    // jQuery
    $("#myCheck).prop("indeterminate", true);
    

    表单数据 -模型绑定将始终限制为请求中实际发送的值,无论是从url还是数据负载(在POST上)。

    在这个简化的示例中,未选中和不确定复选框的处理方式相同:

    Checkbox state and post data

    你可以在这里亲自确认 堆栈段 :

    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通过每个复选框发出两个输入来解决此问题:

    Html.CheckBoxFor

    注意 :隐藏的输入保证即使复选框不是 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