1

所以,标题有点做作,但这本质上是我想要完成的:

<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>

这里的想法是:

  1. 我希望表单Item是部分的,并且我想对 Razor 的 foreach 和为 Knockout 提供模板使用相同的部分。(我不想重复 HTML,也不想为 Knockout 创建一个特殊版本。)

  2. Razor foreach 用于在 JavaScript 被禁用或不支持的情况下进行后备。表单应该仍然是可编辑和可提交的(因为模型绑定器将正确处理 POST 数据),无论是否使用 JavaScript。

  3. 这当然意味着字段名称需要遵循Items[0].SomeField. 但是,部分将被强类型化为 one 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 中将正确的前缀放入部分中。通常,您会使用 afor而不是解决此问题foreach,然后传递索引项而不是通用项,即:

@for (var i = 0; i < Model.Items.Count(); i++)
{
    @Html.TextBoxFor(m => m.Items[i].SomeField)
}

但是,这在使用部分时不起作用,因为一旦进入部分,您仍然只是在处理一个Item脱离上下文的单个 。

所以,最紧迫的问题是我怎样才能仍然使用部分强类型 to Item,但仍然让它知道存在的任何索引?然后,除此之外,如何最好地减少重复或 HTML 和逻辑(增加代码重用)。我的其余想法是否走在正确的轨道上。有什么我可以改进或做得更好的吗?

编辑

我应该提一下,理想情况下,我想保留部分强类型并实际利用它。换句话说,我希望能够做到@Html.TextBoxFor(m => m.SomeField)而不是像@Html.TextBox("Items[" + iterator + "].SomeField"). 如果我必须走那条路,那是一回事,但我希望有更好的方法。

我想我也可以按照以下方式做一些事情:

@Html.TextBox(prefix + "[" + iterator + "]." + Html.NameFor(m => m.SomeField))

然后传prefixiterator入偏。从某种意义上说,这更好,因为我不必处理硬编码的值,但这是很多额外的逻辑,特别是对于具有一堆字段的部分。再次,希望有更好的方法。

4

2 回答 2

1

您的问题指出普遍缺乏关于如何在 asp.net Mvc 中处理前缀的知识,因为在 10 天内没有人能够提出“可接受的”解决方案,尽管有几种方法可以以干净的方式将前缀传递给您的部分看法:

1你可以获得同样的效果

@Html.TextBoxFor(m => m.Items[i].SomeField)

通过使用 DisplayFor 或 EditorFor 调用局部视图:

@Html.DisplayFor(m => m.Items[i].SomeField)

您可以使用与项目数据类型相同的名称定义显示/编辑模板,或者只是将部分视图的名称传递给 EditorFor/DisplayFor 助手。有关更多信息,请参见此处

2您也可以使用与前一个答案相同的 RenderPartial 重载,通过将要使用的完整前缀分配给 ViewDataDictionary 属性:ViewDataDictionary.TemplateInfo.HtmlFieldPrefix

完成此操作后,您提供的前缀将自动添加到所有输入字段名称中。

所以如果你写:

@Html.TextBoxFor(m => m.Property)

文本框的名称为:Prefix.Property,其中 Prefix 是您传入的前缀

ViewDataDictionary.TemplateInfo.HtmlFieldPrefi

于 2013-05-05T15:14:11.507 回答
0

所以,最紧迫的问题是我如何仍然对 Item 使用部分强类型,但仍然让它知道存在的任何索引?

我想你只是想知道数组中的索引,你应该在 ViewDataDictionary 中传递它。

@for (var i = 0; i < Model.Items.Count(); i++){
  Html.RenderPartial("_ItemForm", Model.Items[i], new ViewDataDictionary(){
    { "index", i }
  });
}
于 2013-04-30T17:50:42.940 回答