我目前正在开发一个应用程序,它可以复制类似表格的电子表格。我一直使用 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" [email protected] />
</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 的隐藏单元格以及它们在文本框中输入的任何值吗?然后我可以让模型绑定器拆分这些字符串以拆分阶段名称和日期,解析它们,并在特定日期为阶段分配一个值?或者有更合适的方法吗?
I'm working on an application at the moment that replicates a spreadsheet like table. I've been doing it all with ASP.NET model binding and a combination of ajax and a form POST. I've run into some issues where I think I might be better off creating a custom model binder.
Here's my application:
What we have there are stages of a project, and then a bunch of columns corresponding to each month with the predicted cashflows.
There is a button to the right of that image that lets you add an extra month column, and you can add a stage using the add stage button. Those two are done with AJAX, and then when you submit the form I want it to save all changes in one go via a POST.
I've managed to manoeuvre my way around using HTML helpers, view models and default model binding to get it working with adding columns:
In my main view, I have this to generate the rows/columns in the TBODY
<tbody>
@for (int stageIndex = 0; stageIndex < Model.Stages.Count; stageIndex++)
{
@Html.EditorFor(x => x.Stages[stageIndex])
}
</tbody>
<tfoot>
I have a partial view for CashflowStageViewModel which currently looks like:
@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" [email protected] />
</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>
The model binding works by creating IDs and name's that look like this:
id="Stages_5__Months_4__Amount" name="Stages[5].Months[4].Amount"
This causes some issues as I can add/delete rows between the time that these are generated and the time they are sent back to the server. I always try to keep using the default binding, but now it's holding me back so I think it's time I just created a custom model binder, rather than try dirty hacks to keep the indexes right.
My questions are these:
- Do you agree a custom model binder is the way to go? or is there an easy trick I'm missing
- What do you recommend I do in my custom model binder in terms of the names and ID's of my fields and cells in order to read it back in the http post. Is there some sort of guidance on how you should try and name them? Or should I just do string splitting?
By this I mean, for each cell should I have a hidden with name of STAGENAME_DATE and value of whatever they enter in the text box? and then I can have a model binder split those strings up to split the stage name and the date, parse them, and assign a value to the stage on that certain date? Or is there a more appropriate way of doing it?
发布评论
评论(2)
我建议您查看 Steven Sanderson 的 关于此事的优秀文章。他使用自定义的
Html.BeginCollectionItem
帮助器来负责生成输入字段的正确名称,这样您就不必担心绑定。默认模型绑定器开箱即用。I would recommend you taking a look at the Steven Sanderson's excellent article on this matter. He uses a custom
Html.BeginCollectionItem
helper which takes care of generating proper names of the input fields so that you don't have to worry about the binding. The default model binder will work out of the box.我最终在这里阅读了如何使用自己的任意索引:
http://haacked.com/archive/2008 /10/23/model-binding-to-a-list.aspx
结果,我将视图模型输出更新为以下内容:
我对连接的混乱程度不太满意,但它工作得很好。
I ended up reading about how you can use your own arbritrary indexes here:
http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx
As a result I updated my viewmodel output to be following:
I'm not very happy with how messy the concatenation is, but it works pretty well.