【问题标题】:MVC 5: dictionary in model bound to a series of checkboxes in view?MVC 5:模型中的字典绑定到视图中的一系列复选框?
【发布时间】:2017-05-23 13:37:26
【问题描述】:

当我将模型发布到控制器以保存它时,控制器会得到一个空字典。

哪里出了问题?绑定工作有什么特别的事情要做吗?

我的模型有这个属性:

public Dictionary<int, bool> DictionaryTest { get; set; }

我的控制器在调用视图之前填充了一些数据:

mymodel.DictionaryTest = new Dictionary<int, bool> { { 0, false }, { 1, true }, { 2, false } };

我的视图用这段代码正确显示它:

@Html.CheckBoxFor(m_ => m_.DictionaryTest[0], new { @class = "form-control" })
@Html.CheckBoxFor(m_ => m_.DictionaryTest[1], new { @class = "form-control" })
@Html.CheckBoxFor(m_ => m_.DictionaryTest[2], new { @class = "form-control" })

非常感谢

【问题讨论】:

标签: asp.net-mvc dictionary model-binding


【解决方案1】:

这是一个有效的 ASP.NET MVC 5 示例,它通过在访问元素之前调用简单但不直观的 ToArray() 方法来克服任何 [*] 问题。这允许您使用数组访问器(以及索引)访问字典元素,而不管字典的 Key 类型如何。

我已将此 .cshtml 文件放在我的 Shared/EditorTemplates 文件夹中,因此我现在可以通过调用 @Html.EditorFor(model => model.MyDictionary) 将其与任何 Dictionary 一起使用。

注意:不要尝试使用foreach 循环。 ASP.NET MVC 的模型绑定器确实需要索引,如其他解决方案中所述。

有趣的说明:你实际上可以写一个像foreach(int i in Enumerable.Range(0, Model.Count))这样的foreach循环

@model Dictionary<string, bool>

@* 
  BOOTSTRAP V5 only support row-cols-* until * == 6, 
  extend the class if you need to display more items in a row 
*@

<div class="form-group row row-cols-@Model.Count">
  @for (int i = 0; i < Model.Count(); i++)
  {
    <div class="col form-check">
      <div>
        @Html.LabelFor(model => Model.ToArray()[i].Value, Model.ToArray()[i].Key, htmlAttributes: new { @class = "form-check-label" })

        @Html.HiddenFor(model => Model.ToArray()[i].Key)
        @Html.CheckBoxFor(model => Model.ToArray()[i].Value, new { @class = "form-check-input" })
      </div>
    </div>
  }
</div>

编辑:索引技巧现在在official Blazor documentation(ASP 的小兄弟)中作为'警告' 提及。 (在链接文档的末尾)

【讨论】:

    【解决方案2】:

    为了绑定字典,您需要有类似的帖子值:DictionaryProperty[N].KeyDictionaryProperty[N].Value。因此,您的 Razor 代码需要如下所示:

    @Html.HiddenFor(m_ => m_.DictionaryTest[0].Key)
    @Html.CheckBoxFor(m_ => m_.DictionaryTest[0].Value, new { @class = "form-control" })
    
    @Html.HiddenFor(m_ => m_.DictionaryTest[1].Key)
    @Html.CheckBoxFor(m_ => m_.DictionaryTest[1].Value, new { @class = "form-control" })
    
    @Html.HiddenFor(m_ => m_.DictionaryTest[2].Key)
    @Html.CheckBoxFor(m_ => m_.DictionaryTest[2].Value, new { @class = "form-control" })
    

    但是,如果您的键是整数,那么字典就大材小用了。只需使用一个简单的列表。然后,您的 Razor 代码将按原样运行。

    【讨论】:

    • 在这种情况下,一个简单的列表非常有用。非常感谢。
    • 如果您使用引导程序,附加到复选框的类应该是form-check-input
    【解决方案3】:

    我想我会在这里写一些东西,因为我尝试了接受的答案并最终遇到了编译问题 - 我不了解你们,但 DictionaryTest[index].Key 行对我不起作用 -从什么时候开始数组访问需要Key 属性?!

    对于其他寻找答案的人来说,有一些技巧可以让这一切像魔术一样工作。简而言之,这一切都取决于:

    为了将多个属性绑定到一个对象(可以是任何复杂类型的列表或字典),模型绑定器需要知道哪些属性属于组合在一起 - 否则纠缠不清。

    假设你有一个这样的类型:

    public class TestClass
    {
        public string Prop1 { get; set; }
        public string Prop2 { get; set; }
    }
    

    你希望它绑定到这样的方法中:

    public ActionResult MyAction(List<TestClass> test)
    {
        ...
    }
    

    如果你只是像这样在页面上转储一堆输入:

    <input name="Prop1" value="FirstP1" />
    <input name="Prop2" value="FirstP2" />
    
    <input name="Prop1" value="SecondP1" />
    <input name="Prop2" value="SecondP2" />
    

    模型绑定器如何知道哪些是在一起的?简短的回答:它没有 - 它需要一点帮助。您可能已经知道,关键是给它相关的索引,以便它可以确定要分组的索引:

    <input name="test[0].Prop1" value="FirstP1" />
    <input name="test[0].Prop2" value="FirstP2" />
    
    <input name="test[1].Prop1" value="SecondP1" />
    <input name="test[1].Prop2" value="SecondP2" />
    

    现在模型绑定器可以将前两个 [0] 组合在一起,将接下来的两个 [1] 组合在一起,瞧,我们有一个列表绑定。

    注意: 资深人士会记得,重要的是您的索引保持有序 - 如果它们最终不完整或无序(例如,您最终得到 [0][3][1] 由于某些客户端删除或排序),那么您将面临麻烦,因为模型绑定器会按顺序查找。您可以通过添加一些客户端 JS 来解决此问题,以便在表单保存时按顺序重新分配名称 - 这很有效。或者您可以继续阅读:

    字典呢?

    了解上述内容后,绑定字典实际上非常简单:因为它们实际上只是 List&lt;KeyValuePair&lt;X,Y&gt;&gt;

    <input name="test[0].Key" value="Item1" />
    <input name="test[0].Value" value="Item1Value" />
    
    <input name="test[1].Key" value="Item2" />
    <input name="test[1].Value" value="Item2Value" />
    

    但是,如果您提交的索引是无序的,则同样的问题/限制也适用。这里的一个特定场景是 checkboxes - 如果您有一个 Dictionary&lt;string, bool&gt;(或任何其他键类型)要绑定为复选框,您会记得 HTML 不会在表单中提交未选中的复选框提交。因此,如果您只勾选几个项目,您最终会得到一个部分列表,其中的索引保证是无序的。

    解决方案1:在提交表单之前在客户端做JS事情来重写索引,强制它们是顺序的。完全可行,但很烦人。

    解决方案 2:使用乱序键,帮助模型绑定器了解要查找的键。这很简单:你只需要一个额外的输入来定义索引:

    <input type="hidden" name="test.Index" value="IndexA" />
    <input name="test[IndexA].Key" value="IndexAKey" />
    <input name="test[IndexA].Value" value="IndexAValue" />
    
    <input type="hidden" name="test.Index" value="SomeOtherIndex" />
    <input name="test[SomeOtherIndex].Key" value="SomeOtherIndexKey" />
    <input name="test[SomeOtherIndex].Value" value="SomeOtherIndexValue" />
    

    名称为&lt;collectionName&gt;.Index 的额外输入为模型绑定器提供了将其全部整理出来所需的轻推。对于字典,使用字典键作为自定义索引很方便,这样所有内容都可以对齐 - 感觉有点多余,但确实有效:

    <input type="hidden" name="test.Index" value="IndexAKey" />
    <input name="test[IndexAKey].Key" value="IndexAKey" />       //<-- totally feel like this line doesn't need to be there, but it works
    <input name="test[IndexAKey].Value" value="IndexAValue" />
    

    完整地说,Dictionary[index].Key 的符号完全是需要发生的——但这应该是一个 string,并且在 HTML 输入中是 name 属性——而不是在 Html.HiddenFor() 助手中。

    大家安息吧!

    【讨论】:

    • 您是否尝试过使用 Dictionary 的ElementAt() 方法?而不是DictionaryTest[index].Key 您将拥有DictionaryTest.ElementAt(index).Key 其中index 是一个整数。如果您的密钥不是整数,这可能会对您有所帮助。
    猜你喜欢
    • 2017-05-06
    • 2013-03-06
    • 2013-05-31
    • 2018-11-29
    • 2015-12-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多