Связывание массива viewModels [дубликат]

$ echo 'hello:world:again' |sed 's/:.*//'
hello
25
задан Stephen Muecke 19 January 2015 в 10:52
поделиться

1 ответ

Ваша проблема в том, что частичный renders 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">. Это подробно объясняется в статье Фила Хаака «Связывание модели с списком» . Использование подхода Index обычно лучше, поскольку оно также позволяет удалять элементы из списка (в противном случае было бы необходимо переименовать все существующие элементы управления, чтобы индекс был последовательным).

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

Вариант 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>

Обратите внимание на использование индексатора «поддельных», чтобы предотвратить его привязку к post back («#» и [%]]

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

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

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

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

[HttpPost]
public ActionResult AddDetail(IEnumerable<AdminProductDetailModel> model)
{
  ....
}
50
ответ дан Community 16 August 2018 в 04:34
поделиться
  • 1
    Спасибо за помощь, но я уже решил эту проблему с помощью техники, о которой вы упомянули. – Brajesh 22 January 2015 в 08:06
  • 2
    ах спасибо. Я действительно не дошел до того, что мне нужно, но это, безусловно, пригодится, когда мне это нужно. ;) – Ian 19 August 2016 в 10:50
  • 3
    Любой, кто придет сюда, настоятельно рекомендую помощника BeginItemCollection. Это похоже на обман. – Preston 9 September 2016 в 17:27
  • 4
    В качестве примечания также имеется пакет BeginCollectionItemCore для ASP.NET Core. Работает для меня просто отлично. – Erik Božič 18 January 2017 в 11:05
  • 5
    Я не уверен, что это изменение в BeginCollectionItem, поскольку оно было изначально опубликовано, но оно не упоминается здесь, поэтому я его подниму: BeginCollectionItem запрашивает строковый параметр с именем collectionName. Это должно совпадать с именем коллекции, к которой вы пытаетесь установить ссылку. Например, у моего ParentViewModel есть список с именем ChildViewModels, поэтому в моем Детском частичном представлении у меня будет @using(Html.BeginCollectionItem("ChildViewModels"). – Ryan Taite 11 December 2017 в 20:18
Другие вопросы по тегам:

Похожие вопросы: