[英]Rendered partial view does not match model
因此,我編寫了一些代碼,以允許使用AJAX在ASP.NET MVC中動態地從集合中添加和刪除元素。 將新項目添加到集合中可以按預期工作,但是刪除則不能。 模型集合將按預期進行更新(適當的項目將通過索引刪除),但是呈現的HTML始終顯示最后一個項目已被刪除(而不是指定索引處的項目)。
例如,假設我有以下項目:
當我單擊名為“ Foo”的項目旁邊的“刪除”時,我希望生成的呈現HTML如下所示:
當我通過控制器操作進行調試時,似乎是這種情況,因為模型上的Names集合僅包含那些項。 但是,返回到我的AJAX處理程序的呈現的HTML是:
我以為問題可能與緩存有關,但是我沒有嘗試過(OutputCache指令,在$ .ajax中設置cache:false等)正在工作。
這是代碼:
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>();
}
}
}
這里的明顯問題是在RemoveName方法中。 我可以驗證PartialViewResult的Model屬性是否按我期望的方式反映了集合狀態,但是一旦呈現給客戶端,HTML就會不符合我的期望。
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;
}
}
}
這是我用來渲染名稱列表的編輯器模板。 將新項目添加到集合時,按預期工作。
@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>
}
該腳本處理對AddName和RemoveName的調用。 這里的一切都按我的預期工作。
$('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
的值(如果存在)(而不是實際的屬性值)。 此答案的第二部分將解釋這種現象的原因。
在你的情況下, ModelState
值是
Name[0]: Foo
Name[1]: Bar
Name[2]: Baz
因此,即使您返回的更新模型僅包含Name[0]: Bar
和Name[1]: Baz
, EditorFor()
方法在第一次迭代中仍將檢查Name[0]
的ModelState
值,發現它存在並輸出Foo
。
您可以通過在返回視圖之前使用ModelState.Clear()
來解決此問題(盡管正確的方法是使用PRG模式),但是在您的情況下,這似乎沒有必要,尤其是必須回發整個模型。 您可以簡單地回發該項目的索引或Name
值(或者,如果它是一個復雜的對象,則返回一個ID值),刪除該項目並返回一個JsonResult
指示成功或其他。 然后在ajax success
回調中,從DOM中刪除該項。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.