C# 渲染的局部视图与模型不匹配

C# 渲染的局部视图与模型不匹配,c#,asp.net-mvc,asp.net-mvc-5,C#,Asp.net Mvc,Asp.net Mvc 5,因此,我编写了一些代码,允许在ASP.NET MVC中使用AJAX动态添加和删除集合中的元素。向集合中添加新项的工作方式与预期相同,但删除项的工作方式与预期不同。模型集合按预期更新(相应的项按索引删除),但呈现的HTML始终显示最后一个项已删除(而不是指定索引处的项) 例如,假设我有以下项目: 福 酒吧 巴兹 当我单击名为“Foo”的项旁边的“remove”时,我希望得到的呈现HTML如下所示: 酒吧 巴兹 当我通过控制器操作进行调试时,情况似乎就是这样,因为模型上的Names集合只包含

因此,我编写了一些代码,允许在ASP.NET MVC中使用AJAX动态添加和删除集合中的元素。向集合中添加新项的工作方式与预期相同,但删除项的工作方式与预期不同。模型集合按预期更新(相应的项按索引删除),但呈现的HTML始终显示最后一个项已删除(而不是指定索引处的项)

例如,假设我有以下项目:

  • 酒吧
  • 巴兹
当我单击名为“Foo”的项旁边的“remove”时,我希望得到的呈现HTML如下所示:

  • 酒吧
  • 巴兹
当我通过控制器操作进行调试时,情况似乎就是这样,因为模型上的Names集合只包含这些项。但是,返回到AJAX处理程序的呈现HTML是:

  • 酒吧
我认为问题可能与缓存有关,但我尝试过的(OutputCache指令、在$.ajax中设置cache:false等)都不起作用

代码如下:

DemoViewModel.cs Names.cshtml 这是我用来呈现名称列表的编辑器模板。将新项目添加到集合时按预期工作

@model List<string>

@for (int i = 0; i < Model.Count; i++)
{
   <p>
      @Html.EditorFor(m => m[i]) @Html.ActionLink("remove", "RemoveName", null, new { data_target = "names", data_index = i, @class = "link link-item-remove" })
   </p>
}

这是因为您回发了模型,并且模型的值由
DefaultModeBinder
添加到
ModelState
。生成表单控件的
HtmlHelper
方法(在您的例子中是
@Html.EditorFor(m=>m.Names,“Names”)
)使用
ModelState
中存在的值(而不是实际的属性值)。此行为的原因将在本手册的第二部分中解释

在您的情况下,
ModelState
值为

Name[0]: Foo
Name[1]: Bar
Name[2]: Baz
因此,即使您返回的更新模型只包含
Name[0]:Bar
Name[1]:Baz
,但是
EditorFor()
方法在第一次迭代中将检查
ModelState
Name[0]
,发现它存在并输出
Foo

您可以通过在返回视图之前使用
ModelState.Clear()
来解决这个问题(尽管正确的方法是使用PRG模式),但在您的情况下,这一切似乎都不是必需的,尤其是必须回发整个模型。您可以简单地回发项目的索引或
名称
值(或者如果它是一个复杂对象,则是一个ID值),删除该项目并返回一个
JsonResult
,指示是否成功。然后在ajax
success
回调中,从DOM中删除该项

@model List<string>

@for (int i = 0; i < Model.Count; i++)
{
   <p>
      @Html.EditorFor(m => m[i]) @Html.ActionLink("remove", "RemoveName", null, new { data_target = "names", data_index = i, @class = "link link-item-remove" })
   </p>
}
@model MvcPlayground.Models.DemoViewModel

@{
    ViewBag.Title = "Index";
}

<h2>Index</h2>


@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()

    <div class="form-horizontal">
        <h4>Demo</h4>
        <hr />
        @Html.ValidationSummary(true, "", new { @class = "text-danger" })

       <div class="container-collection" id="names">
         @Html.EditorFor(m => m.Names, "Names")
       </div>
        @Html.ActionLink("Add New", "AddName", "Demo", null, new { data_target = "names", @class = "btn btn-addnew" })

        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="Save" class="btn btn-default" />
            </div>
        </div>
    </div>
}
$('form').on('click', '.btn-addnew', function (e) {
   e.preventDefault();

   var form = $(this).closest('form');
   var targetId = $(this).data('target');

   var target = form.find('#' + targetId);
   var href = $(this).attr('href');

   $.ajax({
      url: href,
      cache: false,
      type: 'POST',
      data: form.serialize()
   }).done(function (html) {
      target.html(html);
   });
});

$('form').on('click', '.link-item-remove', function (e) {
   e.preventDefault();

   var form = $(this).closest('form');
   var targetId = $(this).data('target');

   var target = form.find('#' + targetId);
   var href = $(this).attr('href');

   var formData = form.serialize() + '&index=' + $(this).data('index');

   $.ajax({
      url: href,
      cache: false,
      type: 'POST',
      data: formData
   }).done(function (html) {
      target.html(html);
   });

});
Name[0]: Foo
Name[1]: Bar
Name[2]: Baz