我关注了 Steven Sanderson 的博客 - http://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/
当我尝试添加另一行项目而不是添加行时,它会将我发送到带有返回的部分视图的 url。
如何防止这种情况发生并因此添加实际行?
我在下面的代码以及路径(因为我正在使用区域):
我的WebUI
区域/客户端/MyMoveItems/Index.cshtml
@model IEnumerable<MovinMyStuff.Domain.Entities.MoveItem>
@using MovinMyStuff.WebUI.HtmlHelpers
@{
ViewBag.Title = "Index";
}
<h1>My Move Items</h1>
@using (Html.BeginForm())
{
<table class="move-item">
<tr>
<th>
Item
</th>
<th class="dimension-header">
L
</th>
<th class="dimension-header">
W
</th>
<th class="dimension-header">
H
</th>
<th class="weight-header">
Wt
</th>
<th class="qty-header">
Qty
</th>
<th>
Addt'l Work
</th>
</tr>
<tr>
<td colspan="7">
<div id="editorRows">
@foreach (var item in Model)
{
Html.RenderPartial("_MoveItemEditorRow", item);
}
</div>
</td>
</tr>
</table>
@Html.ActionLink("Add Item", "Add", new { area = "Client" }, new { id = "addItem" })
<input type="submit" value="Finished" />
}
区域/客户端/MyMoveItems/_MoveItemEditorRow.cshtml
@model MovinMyStuff.Domain.Entities.MoveItem
@using MovinMyStuff.WebUI.HtmlHelpers
@using (Html.BeginCollectionItem("moveitems"))
{
<div class="editorRow">
<tr>
<td class="item-name">
@Html.TextBoxFor(model => model.MoveItemType)
@Html.ValidationMessageFor(model => model.MoveItemType)
</td>
<td class="item-dimension">
@Html.EditorFor(model => model.Length)
@Html.ValidationMessageFor(model => model.Length)
</td>
<td class="item-dimension">
@Html.EditorFor(model => model.Width)
@Html.ValidationMessageFor(model => model.Width)
</td>
<td class="item-dimension">
@Html.EditorFor(model => model.Height)
@Html.ValidationMessageFor(model => model.Height)
</td>
<td class="item-weight">
@Html.EditorFor(model => model.Weight)
@Html.ValidationMessageFor(model => model.Weight)
</td>
<td class="item-qty">
@Html.EditorFor(model => model.Quantity)
@Html.ValidationMessageFor(model => model.Quantity)
</td>
<td class="work-items-group">
<table class="work-items">
<tr>
<td>Assembly</td>
<td>
@Html.EditorFor(model => model.Assemble)
@Html.ValidationMessageFor(model => model.Assemble)
</td>
</tr>
</table>
<table class="work-items">
<tr>
<td>Glass</td>
<td>
@Html.EditorFor(model => model.HasGlass)
@Html.ValidationMessageFor(model => model.HasGlass)
</td>
</tr>
</table>
</td>
</tr>
</div>
}
区域/客户端/控制器/MyMoveItemsControllers.cs
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MovinMyStuff.Domain.Entities;
using MovinMyStuff.Domain.Concrete;
namespace MovinMyStuff.WebUI.Areas.Client.Controllers
{
public class MyMoveItemsController : Controller
{
private EFDbContext db = new EFDbContext();
//
// GET: /Client/MyMoveItems/
public ActionResult Index()
{
var moveitems = db.MoveItems.Include(m => m.Move);
return View(moveitems);
}
[HttpPost]
public ActionResult Index(IEnumerable<MoveItem> moveitems)
{
return View("Completed", moveitems);
}
public PartialViewResult Add()
{
return PartialView("_MoveItemEditorRow", new MoveItem());
}
}
}
HtmlHelpers/HtmlPrefixScopeExtension.cs
using System;
using System.Collections.Generic;
using System.Web;
using System.Web.Mvc;
namespace MovinMyStuff.WebUI.HtmlHelpers
{
public static class HtmlPrefixScopeExtensions
{
private const string idsToReuseKey = "__htmlPrefixScopeExtensions_IdsToReuse_";
public static IDisposable BeginCollectionItem(this HtmlHelper html, string collectionName)
{
var idsToReuse = GetIdsToReuse(html.ViewContext.HttpContext, collectionName);
string itemIndex = idsToReuse.Count > 0 ? idsToReuse.Dequeue() : Guid.NewGuid().ToString();
// autocomplete="off" is needed to work around a very annoying Chrome behaviour whereby it reuses old values after the user clicks "Back", which causes the xyz.index and xyz[...] values to get out of sync.
html.ViewContext.Writer.WriteLine(string.Format("<input type=\"hidden\" name=\"{0}.index\" autocomplete=\"off\" value=\"{1}\" />", collectionName, html.Encode(itemIndex)));
return BeginHtmlFieldPrefixScope(html, string.Format("{0}[{1}]", collectionName, itemIndex));
}
public static IDisposable BeginHtmlFieldPrefixScope(this HtmlHelper html, string htmlFieldPrefix)
{
return new HtmlFieldPrefixScope(html.ViewData.TemplateInfo, htmlFieldPrefix);
}
private static Queue<string> GetIdsToReuse(HttpContextBase httpContext, string collectionName)
{
// We need to use the same sequence of IDs following a server-side validation failure,
// otherwise the framework won't render the validation error messages next to each item.
string key = idsToReuseKey + collectionName;
var queue = (Queue<string>)httpContext.Items[key];
if (queue == null) {
httpContext.Items[key] = queue = new Queue<string>();
var previouslyUsedIds = httpContext.Request[collectionName + ".index"];
if (!string.IsNullOrEmpty(previouslyUsedIds))
foreach (string previouslyUsedId in previouslyUsedIds.Split(','))
queue.Enqueue(previouslyUsedId);
}
return queue;
}
private class HtmlFieldPrefixScope : IDisposable
{
private readonly TemplateInfo templateInfo;
private readonly string previousHtmlFieldPrefix;
public HtmlFieldPrefixScope(TemplateInfo templateInfo, string htmlFieldPrefix)
{
this.templateInfo = templateInfo;
previousHtmlFieldPrefix = templateInfo.HtmlFieldPrefix;
templateInfo.HtmlFieldPrefix = htmlFieldPrefix;
}
public void Dispose()
{
templateInfo.HtmlFieldPrefix = previousHtmlFieldPrefix;
}
}
}
}
脚本/mms-custom.js
$("#addItem").click(function () {
$.ajax({
url: this.href,
cache: false,
success: function (html) { $("#editorRows").append(html); }
});
return false;
});
$("a.deleteRow").live("click", function () {
$(this).parents("div.editorRow:first").remove();
return false;
});
视图/共享/_ClientLayout.cshtml
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>@ViewBag.Title</title>
<link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
<script src="@Url.Content("~/Scripts/jquery-1.5.1.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/MicrosoftAjax.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/modernizr-2.5.3.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/mms-custom.js")" type="text/javascript"></script>
@(Html.Telerik().StyleSheetRegistrar()
.DefaultGroup(group => group.Add("telerik.common.css")
.Add("telerik.default.css"))
)
</head>
<body>
<section>
@RenderBody()
</section>
</body>
</html>