【问题标题】:MVC best practice with custom model binding for a certain scenario针对特定场景的自定义模型绑定的 MVC 最佳实践
【发布时间】:2012-01-09 12:09:48
【问题描述】:

我目前正在开发一个应用程序,它可以复制表格等电子表格。我一直在使用 ASP.NET 模型绑定以及 ajax 和表单 POST 的组合来完成这一切。我遇到了一些问题,我认为创建自定义模型绑定器可能会更好。

这是我的申请:

我们有一个项目的各个阶段,然后是一堆对应于每个月的列以及预测的现金流量。

该图像右侧有一个按钮,可让您添加额外的月份列,您可以使用添加阶段按钮添加阶段。这两个是用 AJAX 完成的,然后当您提交表单时,我希望它通过 POST 一次性保存所有更改。

我已经设法使用 HTML 帮助程序、视图模型和默认模型绑定来处理添加列:

在我的主视图中,我有这个来生成 TBODY 中的行/列

        <tbody>
            @for (int stageIndex = 0; stageIndex < Model.Stages.Count; stageIndex++)
            {
                @Html.EditorFor(x => x.Stages[stageIndex])
            }
            </tbody>
        <tfoot>

我有一个 CashflowStageViewModel 的部分视图,目前看起来像:

@model BWInvoicing.Ui.Models.CashflowStageViewModel
<tr class="stageRow">
    @Html.HiddenFor(x => x.Stage)
    <td class="stage">
        <div class="stageWrapper">
            <div>
                @Model.Stage
            </div>
            <div class="stageOptions">
                <img class="deleteRowButton"  src="../../../Content/Images/delete.png" id=@Model.Stage />
            </div>
        </div>
    </td>
    <td>
       @Html.TextBoxFor(x => x.Amount, new { @Class = "totalFee emTotal", CashflowStage = Model.Stage })
    </td>
    <td>
        @Html.TextBox("summation", 0, new { @Class = "emTotal summation", CashflowStage = Model.Stage, @readonly = "true" })
    </td>
    @for (int i = 0; i < Model.Months.Count; i++)
    {
        <td>
            @Html.HiddenFor(x => Model.Months[i].Date)
            @Html.TextBoxFor(x => Model.Months[i].Amount, new { @Class = "prediction", CashflowStage = Model.Stage })
        </td>
    }
    <td class="stageHeadingEnd">
        @Model.Stage
    </td>
</tr>

模型绑定通过创建如下所示的 ID 和名称来工作:

id="Stages_5__Months_4__Amount" name="Stages[5].Months[4].Amount"

这会导致一些问题,因为我可以在生成这些行和将它们发送回服务器之间添加/删除行。我总是尝试继续使用默认绑定,但现在它阻碍了我,所以我认为是时候创建一个自定义模型绑定器,而不是尝试肮脏的黑客来保持索引正确。

我的问题是:

  • 您是否同意自定义模型绑定器是可行的方法?或者有一个我错过的简单技巧
  • 您建议我在自定义模型绑定器中根据字段和单元格的名称和 ID 执行什么操作,以便在 http 帖子中读回它。关于如何尝试命名它们是否有某种指导?还是我应该只进行字符串拆分?

我的意思是,对于每个单元格,我是否应该隐藏一个名称为 STAGENAME_DATE 的单元格以及他们在文本框中输入的任何值?然后我可以让模型绑定器拆分这些字符串以拆分舞台名称和日期,解析它们,并在特定日期为舞台分配一个值?或者有更合适的方法吗?

【问题讨论】:

    标签: asp.net asp.net-mvc model-binding


    【解决方案1】:

    我建议你看看 Steven Sanderson 的excellent article 就此事。他使用了一个自定义的Html.BeginCollectionItem 帮助器,它负责生成输入字段的正确名称,这样您就不必担心绑定。默认模型绑定器将开箱即用。

    【讨论】:

    • 这正是我过去几天一直在寻找的东西。谢谢!
    【解决方案2】:

    我最终在这里阅读了有关如何使用自己的任意索引的信息: http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx

    非序列索引 好吧,这一切都很好,但是会发生什么 当您不能保证提交的值将保持 顺序索引?例如,假设您希望允许删除行 在通过 JavaScript 提交图书列表之前。好消息是 通过引入额外的隐藏输入,您可以允许任意 指数。在下面的示例中,我们提供了一个隐藏输入 我们需要绑定到列表的每个项目的 .Index 后缀。的名字 这些隐藏输入中的每一个都是相同的,因此如前所述, 这将为模型绑定器提供一个很好的索引集合来查看 用于绑定到列表时。

    因此,我将视图模型输出更新为:

    @model BWInvoicing.Ui.Models.CashflowStageViewModel
    <tr class="stageRow">
    
        @* Setup custom indexer to allow for add/remove rows *@
        @Html.Hidden("Index", Model.Stage)
    
        @* Store stage to rebuild view model, use bracket notation to allow default model binder to work correctly *@
        @Html.Hidden("[" + Model.Stage + "].Stage",
                       Model.Stage)
        <td class="stage">
            <div class="stageWrapper">
                <div>
                    @Model.Stage
                </div>
                <div class="stageOptions">
                    <img class="deleteRowButton"  src="../../../Content/Images/delete.png" id=@Model.Stage />
                </div>
            </div>
        </td>
        <td>
            @Html.TextBox("[" + Model.Stage + "].Amount", Model.Amount,
                        new { @Class = "totalFee emTotal", CashflowStage = Model.Stage })
        </td>
        <td>
            @* Readonly javascript display of summation *@ 
            @Html.TextBox("summation", 0,
                        new
                            {
                                @Class = "emTotal summation",
                                CashflowStage = Model.Stage,
                                @readonly = "true"
                            })
        </td>
         @* Output the month inputs, setup the correct indexes *@ 
        @for (int i = 0; i < Model.Months.Count; i++)
        {
            <td>
                @Html.Hidden("[" + Model.Stage + "].Months[" + i + "].Date",
                       Model.Months[i].Date)
                @Html.TextBox("[" + Model.Stage + "].Months[" + i + "].Amount",
                        Model.Months[i].Amount, new { @Class = "prediction", CashflowStage = Model.Stage })
            </td>
        }
        <td class="stageHeadingEnd">
            @Model.Stage
        </td>
    </tr>
    

    我对串联的混乱程度不太满意,但它工作得很好。

    【讨论】:

    • 这是否必须在没有 Javascript 的情况下工作?如果没有,我将通过 jQuery 完成所有操作并将索引信息存储在 HTML data- 属性中,然后组装模型数组以发送回 MVC 来清理它。我只是为一个复杂形式的项目做的;它很棒而且很流畅,避免了所有这些烦人的 ID 问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-01
    • 1970-01-01
    • 2010-10-10
    • 1970-01-01
    • 2012-09-20
    • 1970-01-01
    相关资源
    最近更新 更多