在一个页面上将一个视图模型拆分为多个表单

Posted

技术标签:

【中文标题】在一个页面上将一个视图模型拆分为多个表单【英文标题】:Splitting one view model across multiple forms on one page 【发布时间】:2018-02-14 03:17:47 【问题描述】:

我有一个 ASP.NET MVC 5 应用程序,它对 MVC 使用了更多的 MVVM 方法:它具有用于实体框架模型的“视图模型”(VM)。

我们的场景:

我们有一个带有附带编辑视图的 VM,其中包含大约 7 个引导选项卡。 每个选项卡都包含自己的@using (html.BeginForm(...)) ... 。 当用户切换选项卡时,我们使用 AJAX 来保存其数据(如果有效)。

我们的挑战:

POST Edit(...) 操作需要在每次 AJAX 调用时提交常见的隐藏表单。我们最终会得到重复的隐藏字段,因为它们会在每个表单中重复:

@Html.HiddenFor(model => model.ChildVm.Application_No) 
@Html.HiddenFor(model => model.ChildVm.FormSubmitted)
// other hidden fields...

我们在设置其中一些字段时遇到问题,其中一些字段的值对于每个表单都是唯一的。因为我们通过重复 ID 来破坏 HTML 规则,所以我们必须使用一些丑陋的技巧在 jQuery 中设置它们的值:

// Razor helper method to fetch ASP.NET ID for control
var formSubmittedId = '@Html.ClientIdFor(m => m.ChildVm.FormSubmitted)';

// Tab change handler
$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) 
    var form = $(e.relatedTarget).attr('href');
    var formId = $(e.relatedTarget).attr('href').replace('#', '');
    var previousTab = $(form + '-form');
    $(previousTab).find('#' + formSubmittedId).val(formId);
    //....
);

上述方法有效,但很难看。如何在没有重复 ID 的情况下,在同一个 CSHTML 上的多个表单中包含来自同一个视图模型的表单字段?如果我为字段提供不同的 ID,这不会破坏 Edit 的 POST 操作,它使用来自客户端的 ID 将它们与视图模型服务器端的各自属性相匹配?

谢谢。

【问题讨论】:

帖子查看字段的名称而不是 ID .. 例如 <input id='foo' name='bar' type='text'/> 将在 formdata 中包含 bar 而不是 foo 为什么还需要使用 ID?如果两种不同的形式具有相同的name="ClientId" 并且它们实际上是相同的ClientId 并且它发生了变化,你不希望两者都改变​​吗? $([name=clientid]).val(newid) 你可以有重复的名字attributes(只有重复的id属性是无效的)。而且您不需要任何脚本 - 您可以使用 @Html.HiddenFor(m => m.yourProperty, new id = "" ) 删除 id 属性。但你重复这些输入的事实表明你的设计无论如何都是错误的。 不清楚为什么您甚至需要那些隐藏的输入(您只是向恶意用户开放),如果您需要,您应该在 POST 方法中再次获取这些值。但是,如果您确实需要它们,则无论如何都只需要它们的一个副本(而不是在表单中),因为您进行 ajax 调用以发布数据(您可以序列化相关的表单,并使用 @ 将序列化的输入附加到它上面987654335@方法 如果它只是PK,那么这是必要的(您可以检查用户是否有权使用该ID编辑记录)。 【参考方案1】:

表单回传其成功的表单控件的名称/值对,而不是其id 属性的值。 id 属性的唯一用途是用于 javascript/jquery 和 css 选择器。

为了防止无效的 html(重复的 id 属性),您只需使用删除属性(不需要 ugly 脚本)

@Html.HiddenFor(model => model.ChildVm.Application_No, new  id = "" ) 

但是,一般来说,您应该避免在视图中隐藏输入(与记录 ID 相关的输入除外,尽管该值最好作为路由值添加到您的视图中)。如果您需要 POST 方法中其他隐藏输入的值,那么您应该根据 ID 再次获取记录。

如果出于某种原因,您确实需要在每个 ajax 帖子中回发所有这些附加值,更好的解决方案是将这些输入仅包含在单独的 <form> 元素中,然后使用 .serialize() 方法进行组合值,例如

<form id="hiddeninputs">
    @Html.HiddenFor(model => model.ChildVm.Application_No)
    .... // other hidden inputs
<form>
@using (Html.BeginForm(....))

    .... // form controls for 1st tab

@using (Html.BeginForm(....))

    .... // form controls for 2nd tab

然后在脚本中发布表单

$('form').submit(function() 
    var formdata = $(this).serialize() + '&' + $('#hiddeninputs').serialize;
    $.ajax(
        ....
        data: formdata,
        ....
    );
    return false; // cancel the default submit

您甚至可以完全移除隐藏的输入,然后构造一个对象来回传

var hiddeninputs = 
    'ChildVm.Application_No': '@Model.ChildVm.Application_No',
    ....

并使用

var formdata = $(this).serialize() + '&' + $.param(hiddeninputs, true);

【讨论】:

谢谢,@StephenMuecke。在上面,设置var formdata_应该是+吗? 另外,自定义对象需要在ChildVm.Application_No 周围加上引号,否则会给出Uncaught SyntaxError: Unexpected token . 错误。 胖手指 :) - 并且只有在属性名称复杂时才需要引用 - 即包含 .

以上是关于在一个页面上将一个视图模型拆分为多个表单的主要内容,如果未能解决你的问题,请参考以下文章

django:如何从包含外键的多个模型中制作一个表单

在同一视图中使用多个模型时将表单数据传递给模型

在 ASP.NET MVC 中,仅为视图模型的一个成员创建表单

使用单一视图模型在 MVC Razor 视图上发布多个表单

如何在具有棱镜的Xamarin表单中为contentview创建单独的视图模型?

2个页面可以使用相同的视图模型吗?