我也不得不处理同样的情况。我想出的最简单的解决方案是使用包装模型,类似于:
public class QuestionListModel
{
public IList<QuestionModel> Questions { get; set; }
public IList<QuestionModel> Template
{
get
{
return new List<QuestionModel>
{
new QuestionModel {/* defaults for new question */}
};
}
}
public QuestionListModel()
{
Questions = new List<QuestionModel>();
}
}
重要的部分是具有 Template 属性,它也是一个与您想要的实际模型相同类型的IEnumerable<T>。这样,MVC 将为您完成自动编号。因此,使用此模型,您将获得带有“Questions_0__Title”编号的集合,并且您还会获得一个带有“Template_0__Title”命名的模板行。
我将我的模板行从 UI 中隐藏起来,并在添加新行时使用它。
在 razor 中,您可以像绑定常规问题一样绑定模板,唯一的区别是它会隐藏在 <div> 中。您还需要将问题列表的Count 绑定到隐藏字段。就我而言,我有一个问题的编辑器模板,它使用特定的选择器将其呈现在 <div> 中,以便以后轻松访问。
所以你最终得到的是类似于:
<div class="templateContainer">
<div class="question">
[Template]
</div>
</div>
<div class="items">
[for each of your items]
<div class="question">
[Question]
</div>
</div>
当添加一行时,诀窍是,使用 javascript:
-
从隐藏字段中获取计数,将其递增。
var counter = $("#QuestionsListCount");
var count = parseInt(counter.val());
count++;
-
获取整个模板块,克隆它(例如使用 jquery 的 .clone(true)),将其命名为独特的名称(例如使用步骤 1 中的计数器值),并将其附加到您的问题所在的部分。
var template = $("#templateContainer");
var newItem = template.clone(true);
var newId = "item_" + count;
var newQuestion = newItem.children().first();
newQuestion.attr("id", newId);
newQuestion.appendTo('#items');
-
对于每个项目,例如新附加块中的输入(您可以使用分配给它的新 id 找到它),您将 ids => "Template_0" 替换为 "Questions__count from step 2",并且 names => “模板[0]”和“问题[从第 2 步计算]”。
$("#" + newId + " :input").each(function (index, input) {
input.id = input.id.replace("Template_0", "Questions_" + (count - 1));
input.name = input.name.replace("Template[0]", "Questions[" + (count - 1) + "]");
});
更新计数器的隐藏字段=> counter.val(count);
- ...
- 利润!
现在,关于删除,我的做法是,我的 ViewModel 实际上有一个 IsDeleted 标志,我也将它绑定到问题编辑器模板中的隐藏字段。
这样,删除就像隐藏特定的问题一样简单(问题选择器很方便),您将 IsDeleted 字段设置为 true。
当您通过默认模型绑定器取回整个列表时,您需要丢弃所有已删除的列表(或发出实际删除,具体取决于您的后端数据模型)。
这样我就不必处理识别已删除项目的单独方法,也不必为 UI 中的所有项目重新编号。此外,您还可以让服务器端代码确定删除时实际应该发生的情况(例如验证或撤消操作)。
这是一篇很长的帖子,但实现起来并不难(或长),并且可以很容易地以通用方式重用。
干杯!