向控制器提交多次调用数据的相同局部视图? [英] Submit same Partial View called multiple times data to controller?

查看:23
本文介绍了向控制器提交多次调用数据的相同局部视图?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在视图中添加了一个按钮.单击此按钮时,会添加部分视图.在我的表单中,我可以添加尽可能多的局部视图.提交此表单数据时,我无法将所有部分视图数据发送到控制器.我已经制作了一个具有所有属性的不同模型,并且我已经为我的主模型制作了该模型的列表.任何人都可以给我一些技巧,以便我可以将所有部分视图内容发送到我的控制器吗?

在我看来

<div><input type="button" value="添加字段" id="addField" onclick="addFieldss()"/>

函数 addFieldss(){$.ajax({url: '@Url.Content("~/AdminProduct/GetColorSizeQty")',类型:'获取',成功:功能(结果){var newDiv = $(document.createElement("div")).attr("id", 'CSQ' + myCounter);newDiv.html(结果);newDiv.appendTo("#CSQGroup");我的计数器++;},错误:函数(结果){警报(失败");}});}

在我的控制器中

public ActionResult GetColorSizeQty(){var data = new AdminProductDetailModel();data.colorList = commonCore.getallTypeofList("color");data.sizeList = commonCore.getallTypeofList("size");返回部分视图(数据);}[HttpPost]公共 ActionResult AddDetail(AdminProductDetailModel 模型){....}

在我的部分视图中

@model IKLE.Model.ProductModel.AdminProductDetailModel<div class="editor-field">@Html.LabelFor(model => model.fkConfigChoiceCategorySizeId)@Html.DropDownListFor(model => model.fkConfigChoiceCategorySizeId, Model.sizeList, "--Select Size--")@Html.ValidationMessageFor(model => model.fkConfigChoiceCategorySizeId)

<div class="editor-field">@Html.LabelFor(model => model.fkConfigChoiceCategoryColorId)@Html.DropDownListFor(model => model.fkConfigChoiceCategoryColorId, Model.colorList, "--选择颜色--")@Html.ValidationMessageFor(model => model.fkConfigChoiceCategoryColorId)

<div class="editor-field">@Html.LabelFor(model => model.productTotalQuantity)@Html.TextBoxFor(model => model.productTotalQuantity)@Html.ValidationMessageFor(model => model.productTotalQuantity)

解决方案

您的问题是部分呈现基于单个 AdminProductDetailModel 对象的 html,但您正在尝试回发一个集合.当您动态添加一个新对象时,您会继续添加看起来像 <input name="productTotalQuantity" ..> 的重复控件(由于重复的 id,这也会创建无效的 html 属性),因为它们需要 , 等,以便在回发时绑定到集合.

DefaultModelBinder 要求集合项的索引器从零开始并且是连续的,或者表单值包括 Index=someValue,其中索引器是 someValue(例如 <input name="[ABC].productTotalQuantity" ..><input name="Index" value="ABC">.这个有解释在 Phil Haack 的文章 Model Binding To A 中有详细介绍列表.使用索引方法通常更好,因为它还允许您从列表中删除项目(否则需要重命名所有现有控件以便索引器是连续的).

解决您的问题的两种可能方法.

选项 1

BeginItemCollection 帮助程序用于局部视图.此帮助程序将根据 GUID 为 Index 值呈现隐藏输入.在局部视图和渲染现有项目的循环中都需要它.你的部分看起来像

@model IKLE.Model.ProductModel.AdminProductDetailModel@using(Html.BeginCollectionItem()){<div class="editor-field">@Html.LabelFor(model => model.fkConfigChoiceCategorySizeId)@Html.DropDownListFor(model => model.fkConfigChoiceCategorySizeId, Model.sizeList, "--Select Size--")@Html.ValidationMessageFor(model => model.fkConfigChoiceCategorySizeId)

....}

选项 2

使用假"索引器手动创建表示新对象的 html 元素,将它们放置在隐藏容器中,然后在添加按钮事件中,克隆 html,更新索引器和索引值并将克隆的元素附加到DOM.为了确保 html 是正确的,在 for 循环中创建一个默认对象并检查它生成的 html.此答案中显示了这种方法的示例

//模型的更多属性

注意使用假"索引器来防止这个索引器在回发时被绑定(#"和%"不会匹配,所以它们被 DefaultModelBinder 忽略)

$('#addField').click(function() {var index = (new Date()).getTime();var clone = $('#NewItem').clone();//更新克隆的索引器和索引值clone.html($(clone).html().replace(/[#]/g, '[' + index + ']'));clone.html($(clone).html().replace(/"%"/g, '"' + index + '"'));$('#yourContainer').append(clone.html());}

选项 1 的优点是您将视图强输入到模型中,但这意味着每次添加新项目时都会调用服务器.选项 2 的优点是它的所有客户端都完成了,但是如果您对模型进行任何更改(例如向属性添加验证属性),那么您还需要手动更新 html,从而使维护变得更加困难.

最后,如果您使用客户端验证 (jquery-validate-unobtrusive.js),那么每次向 DOM 添加新元素时都需要重新解析验证器,如 这个答案.

$('form').data('validator', null);$.validator.unobtrusive.parse($('form'));

当然你需要改变你的POST方法来接受一个集合

[HttpPost]public ActionResult AddDetail(IEnumerable模型){....}

I have added a button in my view. When this button is clicked partial view is added. In my form I can add as much partial view as I can. When Submitting this form data I am unable to send all the partial view data to controller. I have made a different model having all the attributes and I have made a list of that model to my main model. Can anyone please give me some trick so that I can send all the partial view content to my controller?

In My View

<div id="CSQGroup">   
</div>
<div>
  <input type="button" value="Add Field" id="addField" onclick="addFieldss()" />
</div>

function addFieldss()
{    
  $.ajax({
    url: '@Url.Content("~/AdminProduct/GetColorSizeQty")',
    type: 'GET',
    success:function(result) {
      var newDiv = $(document.createElement("div")).attr("id", 'CSQ' + myCounter);  
      newDiv.html(result);
      newDiv.appendTo("#CSQGroup");
      myCounter++;
    },
    error: function(result) {
      alert("Failure");
    }
  });
}

In My controller

public ActionResult GetColorSizeQty()
{
  var data = new AdminProductDetailModel();
  data.colorList = commonCore.getallTypeofList("color");
  data.sizeList = commonCore.getallTypeofList("size");
  return PartialView(data);
}

[HttpPost]
public ActionResult AddDetail(AdminProductDetailModel model)
{
  ....
}

In my Partial View

@model IKLE.Model.ProductModel.AdminProductDetailModel
<div class="editor-field">
  @Html.LabelFor(model => model.fkConfigChoiceCategorySizeId)
  @Html.DropDownListFor(model => model.fkConfigChoiceCategorySizeId, Model.sizeList, "--Select Size--")
  @Html.ValidationMessageFor(model => model.fkConfigChoiceCategorySizeId)
</div>
<div class="editor-field">
  @Html.LabelFor(model => model.fkConfigChoiceCategoryColorId)
  @Html.DropDownListFor(model => model.fkConfigChoiceCategoryColorId, Model.colorList, "--Select Color--")
  @Html.ValidationMessageFor(model => model.fkConfigChoiceCategoryColorId)
</div>   
<div class="editor-field">
  @Html.LabelFor(model => model.productTotalQuantity)
  @Html.TextBoxFor(model => model.productTotalQuantity)
  @Html.ValidationMessageFor(model => model.productTotalQuantity)
</div>

解决方案

Your problem is that the partial renders html based on a single AdminProductDetailModel object, yet you are trying to post back a collection. When you dynamically add a new object you continue to add duplicate controls that look like <input name="productTotalQuantity" ..> (this is also creating invalid html because of the duplicate id attributes) where as they need to be <input name="[0].productTotalQuantity" ..>, <input name="[1].productTotalQuantity" ..> etc. in order to bind to a collection on post back.

The DefaultModelBinder required that the indexer for collection items start at zero and be consecutive, or that the form values include a Index=someValue where the indexer is someValue (for example <input name="[ABC].productTotalQuantity" ..><input name="Index" value="ABC">. This is explained in detail in Phil Haack's article Model Binding To A List. Using the Index approach is generally better because it also allows you to delete items from the list (otherwise it would be necessary to rename all existing controls so the indexer is consecutive).

Two possible approaches to your issue.

Option 1

Use the BeginItemCollection helper for your partial view. This helper will render a hidden input for the Index value based on a GUID. You need this in both the partial view and the loop where you render existing items. Your partial would look something like

@model IKLE.Model.ProductModel.AdminProductDetailModel
@using(Html.BeginCollectionItem()) 
{
  <div class="editor-field">
    @Html.LabelFor(model => model.fkConfigChoiceCategorySizeId)
    @Html.DropDownListFor(model => model.fkConfigChoiceCategorySizeId, Model.sizeList, "--Select Size--")
    @Html.ValidationMessageFor(model => model.fkConfigChoiceCategorySizeId)
  </div>
  ....
}

Option 2

Manually create the html elements representing a new object with a 'fake' indexer, place them in a hidden container, then in the Add button event, clone the html, update the indexers and Index value and append the cloned elements to the DOM. To make sure the html is correct, create one default object in a for loop and inspect the html it generates. An example of this approach is shown in this answer

<div id="newItem" style="display:none">

  <div class="editor-field">
    <label for="_#__productTotalQuantity">Quantity</label>
    <input type="text" id="_#__productTotalQuantity" name="[#].productTotalQuantity" value />
    ....
  </div>
  // more properties of your model
</div>

Note the use of a 'fake' indexer to prevent this one being bound on post back ('#' and '%' wont match up so they are ignored by the DefaultModelBinder)

$('#addField').click(function() {
  var index = (new Date()).getTime(); 
  var clone = $('#NewItem').clone();
  // Update the indexer and Index value of the clone
  clone.html($(clone).html().replace(/[#]/g, '[' + index + ']'));
  clone.html($(clone).html().replace(/"%"/g, '"' + index  + '"'));
  $('#yourContainer').append(clone.html());
}

The advantage of option 1 is that you are strongly typing the view to your model, but it means making a call to the server each time you add a new item. The advantage of option 2 is that its all done client side, but if you make any changes to you model (e.g. add a validation attribute to a property) then you also need to manually update the html, making maintenance a bit harder.

Finally, if you are using client side validation (jquery-validate-unobtrusive.js), then you need re-parse the validator each time you add new elements to the DOM as explained in this answer.

$('form').data('validator', null);
$.validator.unobtrusive.parse($('form'));

And of course you need to change you POST method to accept a collection

[HttpPost]
public ActionResult AddDetail(IEnumerable<AdminProductDetailModel> model)
{
  ....
}

这篇关于向控制器提交多次调用数据的相同局部视图?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
其他开发最新文章
热门教程
热门工具
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆