渲染的局部视图与模型不匹配 [英] Rendered partial view does not match model

查看:112
本文介绍了渲染的局部视图与模型不匹配的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

因此,我编写了一些代码,以允许使用AJAX在ASP.NET MVC中动态地从集合中添加和删除元素.将新项目添加到集合中可以按预期工作,但是删除则不能.该模型集合将按预期进行更新(适当的项目将通过索引删除),但是呈现的HTML始终显示最后一个项目已被删除(而不是指定索引处的项目).

So I've written some code to allow adding and removing elements from a collection dynamically in ASP.NET MVC using AJAX. Adding new items to the collection works as expected, but removing does not. The model collection is updated as expected (the appropriate item is removed by index), but the rendered HTML consistently shows that the last item has been removed (rather than the one at the specified index).

例如,假设我有以下物品:

For example, let's say I have the following items:

  • Foo
  • 酒吧
  • 巴兹

当我单击名为"Foo"的项目旁边的删除"时,我希望生成的呈现的HTML如下所示:

When I click "remove" next to the item named "Foo", I'd expect the resulting rendered HTML to look as follows:

  • 酒吧
  • 巴兹

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

When I debug through the controller action, this seems to be the case, as the Names collection on the model only contains those items. However, the rendered HTML that is returned to my AJAX handler is:

  • Foo
  • 酒吧

我认为问题可能与缓存有关,但是我没有尝试过(OutputCache指令,在$ .ajax中设置cache:false等)正在工作.

I thought the issue might have to do with caching, but nothing I've tried (OutputCache directive, setting cache:false in $.ajax, etc) is working.

这是代码:

namespace MvcPlayground.Models
{
   using System;
   using System.Collections.Generic;
   using System.Linq;
   using System.Text;
   using System.Threading.Tasks;

   public class DemoViewModel
   {
      public List<string> Names { get; set; }

      public DemoViewModel()
      {
         Names = new List<string>();
      }
   }
}

DemoController.cs

这里的明显问题是在RemoveName方法中.我可以验证PartialViewResult的Model属性是否按我期望的方式反映了集合状态,但是一旦呈现给客户端,HTML便不符合我的期望.

DemoController.cs

The apparent issue here is in the RemoveName method. I can verify that the Model property of the PartialViewResult reflects the collection state as I expect it, but once rendered to the client the HTML is NOT as I expect it.

namespace MvcPlayground.Controllers
{
   using MvcPlayground.Models;
   using System;
   using System.Collections.Generic;
   using System.Linq;
   using System.Web;
   using System.Web.Mvc;

    public class DemoController : Controller
    {
        // GET: Demo
        public ActionResult Index()
        {
            var model = new DemoViewModel();
            return View(model);
        }

        [HttpPost]
        public ActionResult AddName(DemoViewModel model)
        {
            model.Names.Add(string.Empty);

            ViewData.TemplateInfo.HtmlFieldPrefix = "Names";
            return PartialView("EditorTemplates/Names", model.Names);
        }

        [HttpPost]
        public ActionResult RemoveName(DemoViewModel model, int index)
        {
           model.Names.RemoveAt(index);

           ViewData.TemplateInfo.HtmlFieldPrefix = "Names";
           var result = PartialView("EditorTemplates/Names", model.Names);
           return result;
        }
    }
}

Names.cshtml

这是我用来渲染名称"列表的编辑器模板.将新项目添加到集合中时,按预期方式工作.

Names.cshtml

This is the editor template that I am using to render out the list of Names. Works as expected when adding a new item to the collection.

@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>
}

Index.cshtml

这是加载的初始页面,这里没有什么太复杂的.

Index.cshtml

This is the initial page that is loaded, nothing too complicated here.

@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>
}

Index.js

此脚本处理对AddName和RemoveName的调用.这里的一切都按我的预期工作.

Index.js

This script handles the calls to AddName and RemoveName. Everything here works as I'd expect.

$('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);
   });

});

推荐答案

之所以这样,是因为您回发了模型,并且模型的值已由DefaultModeBinder添加到ModelState.生成表单控件的HtmlHelper方法(在您的情况下为@Html.EditorFor(m => m.Names, "Names"))使用ModelState中的值(如果存在)(而不是实际的属性值).此行为的原因在

The reason for this is because you posting back you model, and the values of your model are added to ModelState by the DefaultModeBinder. The HtmlHelper methods that generate form controls (in your case @Html.EditorFor(m => m.Names, "Names")) use the values from ModelState if they exist (rather that the actual property values). The reason for this behavior is explained in the second part of this answer).

在您的情况下,ModelState值为

Name[0]: Foo
Name[1]: Bar
Name[2]: Baz

因此,即使更新后的模型您返回的仅包含Name[0]: BarName[1]: Baz,在第一次迭代中,EditorFor()方法仍将检查ModelState的值Name[0],发现其存在并输出Foo.

so even though the updated model your returning contains only Name[0]: Bar and Name[1]: Baz, The EditorFor() method, in the first iteration will check for a ModelState value of Name[0], discover that it exists and output Foo.

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

You could solve this by using ModelState.Clear() before returning the view (although the correct approach is to use the PRG pattern), but in your case none of this seems necessary, especially having to post back the whole model. You could simply post back the index of the item, or the Name value, (or if it was a complex object, then an ID value), remove the item and return a JsonResult indicating success or otherwise. Then in the ajax success callback, remove the item from the DOM.

这篇关于渲染的局部视图与模型不匹配的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆