Передать один и тот же частичный вид, вызываемый несколько раз, на контроллер?

Я добавил кнопку в моем представлении. При нажатии этой кнопки добавляется частичное представление. В моей форме я могу добавить как можно больше частичного представления. При отправке данных формы я не могу отправить все данные частичного просмотра на контроллер. Я создал другую модель, имеющую все атрибуты, и составил список этой модели для моей основной модели. Может кто-нибудь, пожалуйста, дайте мне немного хитрости, чтобы я мог отправить все частичное представление контента на мой контроллер?

По-моему

<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");
    }
  });
}

В моем контроллере

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)
{
  ....
}

В моем частичном взгляде

@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>

1 ответ

Ваша проблема в том, что частичное рендеринг HTML основан на одном AdminProductDetailModel объект, но вы пытаетесь опубликовать коллекцию. Когда вы динамически добавляете новый объект, вы продолжаете добавлять дубликаты элементов управления, которые выглядят как <input name="productTotalQuantity" ..> (это также создает недопустимый HTML из-за дубликата id атрибуты) где они должны быть <input name="[0].productTotalQuantity" ..>, <input name="[1].productTotalQuantity" ..> и т. д. для того, чтобы связать с коллекцией по почте обратно.

DefaultModelBinder требуется, чтобы индексатор для элементов коллекции начинался с нуля и был последовательным, или чтобы значения формы включали Index=someValue где индексатор someValue (например <input name="[ABC].productTotalQuantity" ..><input name="Index" value="ABC">, Это подробно объясняется в статье Фила Хаака " Привязка модели к списку". Использование индексного подхода, как правило, лучше, поскольку он также позволяет удалять элементы из списка (в противном случае необходимо будет переименовать все существующие элементы управления, чтобы индексатор работал последовательно).

Два возможных подхода к вашей проблеме.

Опция 1

Используйте помощник BeginItemCollection для частичного просмотра. Этот помощник сделает скрытый ввод для Index значение основано на GUID. Это необходимо как в частичном представлении, так и в цикле, в котором вы визуализируете существующие элементы. Ваш частичный будет выглядеть примерно так

@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>
  ....
}

Вариант 2

Вручную создайте элементы html, представляющие новый объект с помощью "поддельного" индексатора, поместите их в скрытый контейнер, затем в событии кнопки "Добавить" клонируйте html, обновите индексаторы и значение индекса и добавьте клонированные элементы в DOM. Чтобы убедиться в правильности HTML, создайте один объект по умолчанию в for Цикл и проверить HTML, который он генерирует. Пример такого подхода показан в этом ответе

<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>

Обратите внимание на использование "фальшивого" индексатора для предотвращения его привязки к посту обратно ("#" и "%" не совпадают, поэтому они игнорируются 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());
}

Преимущество варианта 1 заключается в том, что вы строго набираете представление для своей модели, но это означает, что вы делаете вызов серверу каждый раз, когда добавляете новый элемент. Преимущество варианта 2 заключается в том, что все это делается на стороне клиента, но если вы вносите какие-либо изменения в модель (например, добавляете атрибут проверки в свойство), то вам также необходимо вручную обновить html, что усложняет обслуживание.

Наконец, если вы используете проверку на стороне клиента (jquery-validate-unobtrusive.js), вам нужно будет повторно анализировать валидатор каждый раз, когда вы добавляете новые элементы в DOM, как объяснено в этом ответе.

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

И, конечно, вам нужно изменить метод POST, чтобы принять коллекцию

[HttpPost]
public ActionResult AddDetail(IEnumerable<AdminProductDetailModel> model)
{
  ....
}
Другие вопросы по тегам