1

我正在将一个模型传递给一个视图,该视图的属性是书籍的集合。在我看来,我正在使用 foreach 循环来创建我收藏中的书籍表,每本书都有名字、作者等。

我希望用户能够在客户端添加/编辑/删除书籍。然后我希望能够将模型传递回控制器,其中包含反映所做更改的书籍集合。

这可能吗?

4

3 回答 3

1

在不使用 ajax/jquery/knockout 的情况下解决了它:

基本上我需要将 cshtml 页面包装在 @using (Html.BeginForm(~)){} 标记中,然后使用 for 循环(不是 foreach)来显示列表中的每个项目。然后我需要为列表中的每个项目创建一个@Html.HiddenFor。当我提交表单时,我将模型作为参数,并且项目填充列表。我无法显示我的实际代码,所以我匆忙重新标记了一些关键变量,所以我希望你们能理解它,但这基本上是我让它工作的方式

这是控制器

[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);
 }
于 2013-08-15T02:11:39.990 回答
0

对的,这是可能的

假设您有一个表格,并且您有一个名为“Books”的集合。如果你想以编程方式添加新书,你可以使用 jQuery 和 ajax。拳头你需要一些帮助类。

以下课程可帮助您创建独特的书籍项目以添加到您视图中的书籍集合中。如您所知,每个字段都应该有唯一的前缀,所以模型 binder van 区分表单元素

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 的操作,它返回一个我之前提到的部分视图,然后它在页面中的其他书籍之后加载,然后为了应用不显眼的验证,我们使用最后的树行。

于 2013-08-14T15:57:08.643 回答
0

是的,绝对有可能。我目前在客户端上使用 KnockOut,它将允许您绑定到 Javascript 对象的集合,将每个项目呈现为模板,添加到,从中删除,然后将整个集合发布回服务器进行处理。您将需要处理已删除书籍的状态等事情,并将它们隐藏在装订中,但这一切都是可行的。

这是您在视图中需要的 KO 语法:

<table>
   <!-- ko foreach: {data: books } -->
   <tr>
      <td data-bind="text: title" />
   </tr>
   <!-- /ko -->
</table>

这将为书籍中的每个项目创建一个包含 1 行的表。这些对象需要一个“标题”属性,这将是表中的唯一值。

Knockout 是一个很棒的库,最近我在学习和开发中获得了很多乐趣。您可以在此处获取有关其项目页面的更多信息:http: //knockoutjs.com/

于 2013-08-14T15:58:05.443 回答