将模型返回到具有列表属性的控制器

本文关键字:列表 属性 控制器 模型 返回 | 更新日期: 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的操作,它返回我前面提到的部分视图,然后在页面中的其他书籍之后加载它,然后为了应用不引人注目的验证,我们使用最后一行树。