所以,标题有点人为,但这基本上就是我想要完成的事情:
<ul data-bind="template: { name: 'ItemFormTemplate', foreach: Items }">
@foreach (var item in Model.Items)
{
Html.RenderPartial("_ItemForm", item);
}
</ul>
<script type="text/html" id="ItemFormTemplate">
@{ Html.RenderPartial("_ItemForm", new Item()); }
</script>
这里的想法是:
我希望Item
的形式是部分的,我想对Razor的foreach和Knockout模板使用相同的部分。 (我不想重复HTML或只为Knockout创建一个特殊版本。)
如果JavaScript被禁用或不受支持,则Razor foreach用于后备。无论是否使用JavaScript,表单仍应可编辑且可提交(如在模型绑定器中将正确处理POST数据)。
这当然意味着字段名称需要遵循Items[0].SomeField
之类的模式。部分将仅针对一个Item
强类型(而不是IEnumerable<Item>
)。可以理解,这对于Knockout模板来说实际上是不可能的,因为当前索引只能在客户端可用。我有一个我已经在使用的解决方法:
ko.bindingHandlers.prefixAndIndex = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
if (bindingContext.$index) {
var prefix = ko.utils.unwrapObservable(valueAccessor());
var $element = $(element);
$element.find('input,textarea,select').each(function () {
var $this = $(this);
var newId = prefix + '_' + bindingContext.$index() + '__' + $this.attr('id');
var newName = prefix + '[' + bindingContext.$index() + '].' + $this.attr('name');
$('label[for=' + $this.attr('id') + ']').attr('for', newId);
$this.attr('id', newId);
$this.attr('name', newName);
});
}
}
};
这只是绑定到模板的顶级元素(在本例中为<li>
),并使用正确的前缀搜索并替换所有id和name属性。它似乎运作良好,但任何批评或改进建议都欢迎。
真的,我现在唯一正在努力的是在Razor foreach内部获得正确的前缀。通常,您使用for
而不是foreach
解决此问题,然后传递索引项而不是通用项,即:
@for (var i = 0; i < Model.Items.Count(); i++)
{
@Html.TextBoxFor(m => m.Items[i].SomeField)
}
但是,当使用partial时,这不起作用,因为一旦你进入局部,你仍然只是处理单个Item
,脱离了上下文。
所以,最紧迫的问题是我如何仍然使用部分强类型Item
,但仍然知道任何索引存在?然后,除此之外,如何最好地减少重复或HTML和逻辑(增加代码重用)。我的其余思想是否在正确的轨道上。有什么我可以改进或做得更好吗?
修改
我应该提到理想情况我想保持部分强类型并实际利用它。换句话说,我希望能够@Html.TextBoxFor(m => m.SomeField)
而不是像@Html.TextBox("Items[" + iterator + "].SomeField")
那样伪造它。如果我有去那条路,这是一回事,但我希望有更好的方法。
我想我也可以做一些事情:
@Html.TextBox(prefix + "[" + iterator + "]." + Html.NameFor(m => m.SomeField))
然后将prefix
和iterator
传递给部分内容。从某种意义上讲,我不需要处理硬编码值,这是更好的,但它是一个额外逻辑的很多,特别是对于有一堆字段的部分。再次,希望有更好的方式。
答案 0 :(得分:1)
你的问题指出在asp.net Mvc中如何处理前缀的分散缺乏知识,因为在10天内没有人能够提出一个“可接受的”解决方案,因为有几种方法可以以干净的方式传递前缀你的部分观点:
1 您可能会获得与
相同的效果@Html.TextBoxFor(m => m.Items[i].SomeField)
使用DisplayFor或EditorFor调用局部视图:
@Html.DisplayFor(m => m.Items[i].SomeField)
您可以使用item数据类型的相同名称定义显示/编辑模板,或者只是将部分视图的名称传递给EditorFor / DisplayFor帮助程序。有关详细信息,请参阅here。
2 您也可以使用前一个答案的RenderPartial的相同重载,将您要使用的完整前缀传递给ViewDataDictionary,方法是将其分配给属性: ViewDataDictionary.TemplateInfo.HtmlFieldPrefix
完成此操作后,您提供的前缀将自动添加到所有输入字段名称。
所以如果你写:
@Html.TextBoxFor(m => m.Property)
文本框的名称为:Prefix.Property,其中前缀是您传入的前缀
ViewDataDictionary.TemplateInfo.HtmlFieldPrefi
答案 1 :(得分:0)
所以,最紧迫的问题是我如何仍然使用偏爱 强类型到项目,但仍然知道任何索引 本?
我想你只想知道数组中的索引,你应该在ViewDataDictionary中传递它。
@for (var i = 0; i < Model.Items.Count(); i++){
Html.RenderPartial("_ItemForm", Model.Items[i], new ViewDataDictionary(){
{ "index", i }
});
}