问题
I followed Steven Sanderson's blog - http://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/
When I try to add another row of items, rather than adding the row it sends me to the url with the returned partial view.
How do I prevent this from happening and hence add an actual row?
My code below along with the path (as I am using Areas):
MyWebUI
Areas/Client/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" />
}
Areas/Client/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>
}
Areas/Client/Controllers/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;
}
}
}
}
Scripts/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;
});
Views/Shared/_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>
回答1:
Try this:
First: Make sure your jQuery code (Scripts/mms-custom.js
) is inside $(document.ready
-- it does not look like it is if that is if you posted your entire file above.
If that doesn't fix it by itself, try this:
Second:
$("#addItem").click(function () {
$.ajax({
url: this.href,
cache: false,
success: function (data) { $("#editorRows").append(data); return false;}
});
return false;
});
in place of your code:
$("#addItem").click(function () {
$.ajax({
url: this.href,
cache: false,
success: function (html) { $("#editorRows").append(html); }
});
return
I had a similar problem when using that blog post as a point of reference.
来源:https://stackoverflow.com/questions/11387036/mvc3-razor-editing-a-variable-length-list