将模型返回到具有列表属性的控制器
本文关键字:列表 属性 控制器 模型 返回 | 更新日期: 2023-09-27 18:27:36
我正在将一个模型传递给一个视图,该视图的属性是书籍的集合。在我看来,我正在使用foreach循环来创建我收藏的书的表格,每本书都有名字、作者等。
我希望用户能够在客户端添加/编辑/删除书籍。然后,我希望能够将模型传递回控制器,其中图书集合反映所做的更改。
这可能吗?
在不使用ajax/jquery/knocket:的情况下解决了它
基本上,我需要将cshtml页面包装在@using(Html.BeginForm(~)){}标记中,然后使用for循环(而不是foreach)来显示列表中的每个项。然后我需要为列表中的每个项目创建一个@Html.HiddFor。当我提交表单时,我将模型作为参数,项目填充列表。我无法显示我的实际代码,所以我匆忙地重新标记了一些关键变量,所以我希望你们能理解它,但这本质上是我让它工作的方式
这是控制器
[HttpGet]
public ActionResult BookStore(int storeId)
{
//model contains a list property like the following:
//public List<Books> BooksList { get; set; }
//pass the model to the view
var model = new BookStoreModel();
return View(model);
}
这是视图
@model BookStore.Models.BookStoreModel
@using (Html.BeginForm("BookStoreSummary", "BookStore", FormMethod.Post))
{
<fieldset>
@Html.HiddenFor(model => model.Id)
@Html.HiddenFor(model => model.BookId)
@Html.HiddenFor(model => model.LastModified)
//some html stuff here
<table id="users" class="ui-widget ui-widget-content">
<thead>
<tr class="ui-widget-header BookTable">
<th>@Html.DisplayNameFor(model => model.BookList.FirstOrDefault().Title) </th>
<th>@Html.DisplayNameFor(model => model.BookList.FirstOrDefault().Author)</th>
</tr>
</thead>
<tbody>
@for (int i = 0; i < Model.BookList.Count; i++)
{
<tr>
<td>
@Html.HiddenFor(model => model.BookList[i].Author)
@Html.DisplayFor(model => model.BookList[i].Author)
</td>
<td>
@Html.HiddenFor(model => model.BookList[i].BookId)
@Html.HiddenFor(model => model.BookList[i].Title)
@Html.DisplayFor(model => model.BookList[i].Title)
</td>
</tr>
}
</tbody>
</table>
</fieldset>
}
以及后置控制器:
[HttpPost]
//[AcceptVerbs("POST")]
public ActionResult BookStoreSummary(BookStoreModel model)
{
//do stuff with model, return
return View(model);
}
是的,绝对可能。我目前在客户端上使用KnockOut,它将允许您绑定到Javascript对象的集合,将每个项呈现为模板,添加到,从中删除,然后将整个集合发布回服务器进行处理。您需要处理诸如已删除书籍的状态之类的事情,并将它们从绑定中隐藏起来,但这一切都是可行的。
以下是您在视图中需要的KO语法:
<table>
<!-- ko foreach: {data: books } -->
<tr>
<td data-bind="text: title" />
</tr>
<!-- /ko -->
</table>
这将为书籍中的每个项目创建一个表,其中包含一行。。这些对象需要一个"title"属性,该属性将是表中的唯一值。
Knockout是一个很棒的图书馆,最近我用它学习和发展了很多乐趣。您可以在他们的项目页面上获得更多信息:http://knockoutjs.com/
是的,可能是
假设你有一张表格,你有一本名为"书"的收藏品。如果您想以编程方式添加新书,可以使用jQuery和ajax。首先你需要一些助手类。
以下类帮助您创建独特的图书项目,以添加到视图中的图书集合中。正如你所知,每个字段都应该有唯一的前缀,所以模型绑定器可以区分形式元素
public static class HtmlClientSideValidationExtensions
{
public static IDisposable BeginAjaxContentValidation(this HtmlHelper html, string formId)
{
MvcForm mvcForm = null;
if (html.ViewContext.FormContext == null)
{
html.EnableClientValidation();
mvcForm = new MvcForm(html.ViewContext);
html.ViewContext.FormContext.FormId = formId;
}
return new AjaxContentValidation(html.ViewContext, mvcForm);
}
private class AjaxContentValidation : IDisposable
{
private readonly MvcForm _mvcForm;
private readonly ViewContext _viewContext;
public AjaxContentValidation(ViewContext viewContext, MvcForm mvcForm)
{
_viewContext = viewContext;
_mvcForm = mvcForm;
}
public void Dispose()
{
if (_mvcForm != null)
{
_viewContext.OutputClientValidation();
_viewContext.FormContext = null;
}
}
}
}
public static class CollectionValidation
{
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;
}
}
}
然后我们假设您对添加这样的书籍有部分看法:
@model Models.Book
@using (Html.BeginAjaxContentValidation("form"))
{
using (Html.BeginCollectionItem("Books"))
{
<div class="fieldcontanier">
@Html.LabelFor(model => model.Title)
@Html.EditorFor(model => model.Title)
@Html.ValidationMessageFor(model => model.Title)
</div>
<div class="fieldcontanier">
@Html.LabelFor(model => model.Author)
@Html.EditorFor(model => model.Author)
@Html.ValidationMessageFor(model => model.Author)
</div>
...
}
}
假设表单中有一个"添加新书"链接,您已经在jQuery中为其定义了以下事件:
$('a ').click(function (e) {
e.preventDefault();
$.ajax({
url: '@Url.Action("NewBook")',
type: 'GET',
success: function (context) {
$('#books').append(context);
$("form").removeData("validator");
$("form").removeData("unobtrusiveValidation");
$.validator.unobtrusive.parse("form");
}
});
});
在上面的代码中,首先您请求一个名为NewBook的操作,它返回我前面提到的部分视图,然后在页面中的其他书籍之后加载它,然后为了应用不引人注目的验证,我们使用最后一行树。