MVC 5:模型中的字典绑定到视图中的一系列复选框?
MVC 5: dictionary in model bound to a series of checkboxes in view?
当我POST将我的模型保存到控制器时,控制器得到一个空字典。
哪里出了问题?绑定工作有什么特别的吗?
我的模型有这个 属性:
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" })
非常感谢
为了绑定字典,您需要有 post 个值,例如:DictionaryProperty[N].Key
和 DictionaryProperty[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 代码将按原样运行。
我想我应该在这里写点东西,因为我尝试了接受的答案并最终遇到了编译问题 - 我不了解你们,但是 DictionaryTest[index].Key
行就是不知道为我工作 - array-accessing 什么时候需要 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]
由于一些 client-side 删除或排序)然后你会遇到麻烦,因为模型活页夹会按顺序查找。您可以通过添加一些 client-side JS 以在 form-save 上按顺序重新分配名称来解决此问题 - 这很有用。或者您可以继续阅读:
字典呢?
一旦你理解了上面的内容,让字典绑定其实很简单:假设它们真的只是一个 List<KeyValuePair<X,Y>>
:
<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" />
但是,如果您提交的索引是 out-of-order,则相同的 issues/limitations 适用。这里的一个特定场景是 checkboxes - 如果你有一个 Dictionary<string, bool>
(或任何其他键类型)你想绑定为复选框,你会记得 HTML 不会在表单提交中提交未选中的复选框。因此,如果您只勾选几个项目,您最终会得到一个部分列表,其中的索引肯定是乱序的。
解决方案 1:做 JS 的事 client-side 在表单提交之前重写索引,强制它们是顺序的。完全可行,但很烦人。
解决方案 2:使用 out-of-order 键,帮助模型绑定器了解要查找的键。这很简单:你只需要一个额外的输入,来定义索引:
<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" />
名称为 <collectionName>.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" />
为了 full-circle,Dictionary[index].Key
的符号完全是需要发生的 - 但那应该是 string
,并且在 HTML 输入 name
属性 - 不在 Html.HiddenFor()
助手中。
大家安息吧!
这是一个有效的 ASP.NET MVC 5 示例,它通过在访问元素之前调用简单但不直观的 ToArray()
方法来克服任何 [*]
问题。这允许您使用数组访问器(以及索引)访问字典元素,而不管您的字典的键类型如何。
我已将此 .cshtml
文件放在我的 Shared/EditorTemplates
文件夹中,因此我现在可以通过调用 @Html.EditorFor(model => model.MyDictionary)
.[=19= 将它与任何 Dictionary 一起使用]
注意 :不要尝试使用 foreach
循环。 ASP.NET MVC 的模型绑定器确实需要索引,如其他解决方案中所述。
趣味笔记:你实际上可以写一个 foreach 循环,比如 foreach(int i in Enumerable.Range(0, Model.Count))
@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的小弟)中作为'Warning'。 (在链接文档的末尾)
当我POST将我的模型保存到控制器时,控制器得到一个空字典。
哪里出了问题?绑定工作有什么特别的吗?
我的模型有这个 属性:
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" })
非常感谢
为了绑定字典,您需要有 post 个值,例如:DictionaryProperty[N].Key
和 DictionaryProperty[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 代码将按原样运行。
我想我应该在这里写点东西,因为我尝试了接受的答案并最终遇到了编译问题 - 我不了解你们,但是 DictionaryTest[index].Key
行就是不知道为我工作 - array-accessing 什么时候需要 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]
由于一些 client-side 删除或排序)然后你会遇到麻烦,因为模型活页夹会按顺序查找。您可以通过添加一些 client-side JS 以在 form-save 上按顺序重新分配名称来解决此问题 - 这很有用。或者您可以继续阅读:
字典呢?
一旦你理解了上面的内容,让字典绑定其实很简单:假设它们真的只是一个 List<KeyValuePair<X,Y>>
:
<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" />
但是,如果您提交的索引是 out-of-order,则相同的 issues/limitations 适用。这里的一个特定场景是 checkboxes - 如果你有一个 Dictionary<string, bool>
(或任何其他键类型)你想绑定为复选框,你会记得 HTML 不会在表单提交中提交未选中的复选框。因此,如果您只勾选几个项目,您最终会得到一个部分列表,其中的索引肯定是乱序的。
解决方案 1:做 JS 的事 client-side 在表单提交之前重写索引,强制它们是顺序的。完全可行,但很烦人。
解决方案 2:使用 out-of-order 键,帮助模型绑定器了解要查找的键。这很简单:你只需要一个额外的输入,来定义索引:
<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" />
名称为 <collectionName>.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" />
为了 full-circle,Dictionary[index].Key
的符号完全是需要发生的 - 但那应该是 string
,并且在 HTML 输入 name
属性 - 不在 Html.HiddenFor()
助手中。
大家安息吧!
这是一个有效的 ASP.NET MVC 5 示例,它通过在访问元素之前调用简单但不直观的 ToArray()
方法来克服任何 [*]
问题。这允许您使用数组访问器(以及索引)访问字典元素,而不管您的字典的键类型如何。
我已将此 注意 :不要尝试使用 趣味笔记:你实际上可以写一个 foreach 循环,比如 编辑 :索引技巧现在在official Blazor documentation(ASP的小弟)中作为'Warning'。 (在链接文档的末尾).cshtml
文件放在我的 Shared/EditorTemplates
文件夹中,因此我现在可以通过调用 @Html.EditorFor(model => model.MyDictionary)
.[=19= 将它与任何 Dictionaryforeach
循环。 ASP.NET MVC 的模型绑定器确实需要索引,如其他解决方案中所述。foreach(int i in Enumerable.Range(0, Model.Count))
@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>