使用Knockout模板集成的部分可编辑集合的Razor表单 [英] Razor form with editable collection using partial with Knockout template integration

查看:102
本文介绍了使用Knockout模板集成的部分可编辑集合的Razor表单的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

因此,标题有点虚构,但这本质上是我想要完成的事情:

So, the title is a little contrived, but this is essentially what I want to accomplish:

<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或只为淘汰赛创建特殊版本.)

  1. I want the form for Item to be a partial and I want to use the same partial for both Razor's foreach and feeding Knockout a template. (I don't want to repeat the HTML or have to create a special version just for Knockout.)

Razor foreach用于回退,以防禁用或不支持JavaScript.不论是否带有JavaScript,该表单仍应是可编辑且可提交的(因为在模型绑定器中将正确处理POST数据).

The Razor foreach is for fallback in case JavaScript is disabled or not supported. The form should still be editable and submittable (as in the model binder will process the POST data correctly) with or without JavaScript.

这当然意味着字段名称将遵循类似Items[0].SomeField的模式.不过,将仅对一个Item(而不是IEnumerable<Item>)强力键入partial.可以理解,对于Knockout模板,这实际上是不可能的,因为当前索引仅在客户端可用.我已经有一种解决方法:

This of course means that the field names will need to follow the a pattern like Items[0].SomeField. The partial will be strongly typed for just one Item, though (rather than IEnumerable<Item>). Understandably this won't really be possible for the Knockout template, since the current index will only be available client-side. I have a workaround I'm already using for that:

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属性.看起来效果很好,但是欢迎提出任何批评或建议.

This just binds to the top-level element of the template (the <li> in this case) and searches and replaces all id and name attributes with the proper prefix. It seems to work pretty well, but any critiques or suggestions for improvement welcome.

真的,我现在唯一要做的就是在Razor foreach内将适当的前缀添加到partial中.通常,您可以使用for而不是foreach来解决此问题,然后传递索引项而不是仅传递通用项,即:

Really, the only thing I'm struggling with right now is getting the proper prefix into the partial when inside the Razor foreach. Typically you'd solve this using a for instead of foreach and then passing the indexed item instead of just a generic item, i.e.:

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

但是,当使用局部时,这是行不通的,因为一旦您进入局部,您仍然会在上下文之外处理单个Item.

But, this doesn't work when using a partial, because once you're into the partial, you're still just dealing with a single Item, out of context.

因此,最紧迫的问题是我如何仍可以对Item使用部分强类型,但仍然知道存在任何索引?然后,除此之外,如何最好地减少重复或HTML和逻辑(增加代码重用).我的其余想法是否在正确的轨道上?有什么我可以改善或改善的吗?

So, the most pressing question is how can I still use a partial strongly-type to Item, but still have it be aware of any index present? Then, aside from that, how to best reduce duplication or HTML and logic (increase code reuse). Am I on the right track with the rest of my thinking. Is there anything I can improve or do better?

编辑

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

I should mention that ideally I want to keep the partial strongly-typed and actually take advantage of that. In other words, I want to be able to do @Html.TextBoxFor(m => m.SomeField) rather than faking it like @Html.TextBox("Items[" + iterator + "].SomeField"). If I have to go that route, that's one thing, but I'm hoping there's a better way.

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

I suppose I could also do something along the lines of:

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

,然后将prefixiterator传递到局部.从某种意义上说,我不必处理硬编码的值,这更好,但是这是一种 lot 额外的逻辑,特别是对于具有一堆字段的局部逻辑.再次,希望有更好的方法.

And then pass prefix and iterator into the partial. That's better in the sense that I'm not having to deal with hard-coded values, but it's a lot of extra logic going on especially for a partial with a bunch of fields. Again, hoping for a better way.

推荐答案

您的问题指出,关于在asp.net Mvc中如何处理前缀的知识分散存在,因为在10天之内没有人能够提出可竞争的"建议虽然没有解决方案,但是有几种方法可以将前缀以干净的方式传递给局部视图:

Your question pointed out a diffused lack of knowledge about how prefixes are handled in asp.net Mvc, because in 10 days none was able to propose an "accettable" solution nothwitstanding there are several ways to pass the prefix in a clean way to your partial view:

1 您可能会获得与

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

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

by using either DisplayFor or EditorFor to call the partial view:

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

您可以使用与项目数据类型相同的名称定义显示/编辑模板,也可以仅将部分视图的名称传递给EditorFor/DisplayFor帮助器.有关更多信息,请参见这里.

You may either define a display/edit template with the same name of the item datatype, or simply passing the name of the partial view to the EditorFor/DisplayFor helpers. For more information see here.

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

2 You may also use the same overload of the RenderPartial of a previous answer to pass the complete prefix you want to use to the ViewDataDictionary by assigning it to the property: ViewDataDictionary.TemplateInfo.HtmlFieldPrefix

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

Once you have done this the prefix you provided will be automatically added to all input fields names.

因此,如果您写:

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

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

the textbox will have the name: Prefix.Property, where Prefix is the Prefix you passed in

ViewDataDictionary.TemplateInfo.HtmlFieldPrefi

ViewDataDictionary.TemplateInfo.HtmlFieldPrefi

这篇关于使用Knockout模板集成的部分可编辑集合的Razor表单的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆