Как работает MVC 4 List Binding?
Если мне нужен набор входов в форме для привязки к List
в MVC 4, я знаю, что следующее соглашение об именах для атрибутов input
name
будет работать:
<input name="[0].Id" type="text" />
<input name="[1].Id" type="text" />
<input name="[2].Id" type="text" />
Но мне любопытно, как прощается модельное связующее. Например, как насчет следующего:
<input name="[0].Id" type="text" />
<input name="[3].Id" type="text" />
<input name="[8].Id" type="text" />
Как бы справиться с этим связующим устройством модели? Будет ли он привязан к List
длины 9 с нулями? Или он будет привязан к List
длины 3? Или это вообще задохнется?
Почему я забочусь
Я хочу реализовать динамическую форму, в которой пользователь может добавлять строки в форму, а также может удалять строки из формы. Поэтому, если я удаляю строку 2 из 8 общих строк, я хочу знать, нужно ли мне перенумеровать все последующие входы.
Ответы
Ответ 1
Существует определенный формат проводки для использования с коллекциями. Об этом говорится в блоге Скотта Хансельмана:
http://www.hanselman.com/blog/ASPNETWireFormatForModelBindingToArraysListsCollectionsDictionaries.aspx
Еще одна запись в блоге от Фила Хаака рассказывает об этом здесь:
http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx
Наконец, запись в блоге, которая делает именно то, что вы хотите здесь:
http://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/
Ответ 2
Я следил за этим подходом, связанным в блогах выше, и добавил несколько подробностей, которые могут быть полезны для некоторых - особенно, поскольку я хотел динамически добавлять любое количество строк, но не хотел использовать AJAX для этого (мне нужна форма только представить в почте). Я также не хотел беспокоиться о сохранении последовательных идентификаторов. Я записывал список дат начала и окончания:
Показать модель:
public class WhenViewModel : BaseViewModel {
public List<DateViewModel> Dates { get; set; }
//... Other properties
}
Дата начала/окончания Модель просмотра:
public class DateViewModel {
public string DateID { get; set; }
public DateTime? StartDate { get; set; }
public DateTime? EndDate { get; set; }
}
Затем, используя их на странице (с datepicker):
<div class="grid-8-12 clear" id="DatesBlock">
@{
foreach (DateViewModel d in Model.Dates) {
@:<div class="grid-5-12 left clear">
@Html.Hidden("Dates.Index", d.DateID)
@Html.Hidden("Dates[" + d.DateID + "].DateID", d.DateID) //ID again to populate the view model
@Html.TextBox("Dates[" + d.DateID + "].StartDate",
d.StartDate.Value.ToString("yyyy-MM-dd"))
@:</div>
@:<div class="grid-5-12">
@Html.TextBox("Dates[" + d.DateID + "].EndDate",
d.EndDate.Value.ToString("yyyy-MM-dd"))
@:</div>
<script type="text/javascript">
$('input[name="Dates[@d.DateID].StartDate"]')
.datepicker({ dateFormat: 'yy-mm-dd'});
$('input[name="Dates[@d.DateID].EndDate"]')
.datepicker({dateFormat: 'yy-mm-dd'});
</script>
}
}
</div>
<a href="#" onclick="AddDatesRow()">Add Dates</a>
Как описано в сообщении в блоге, описанном выше в статье @ErikTheVikings, коллекция создается повторяющимся скрытым элементом: @Html.Hidden("Dates.Index", d.DateID)
для каждой записи в коллекции на странице.
Я хотел произвольно добавить строки, не используя AJAX, чтобы отправить данные обратно на сервер, который я сделал, создав скрытый div, содержащий шаблон одной "строки" /элемента в коллекции:
Скрытая строка "Шаблон":
<div id="RowTemplate" style="display: none">
<div class="grid-5-12 clear">
@Html.Hidden("Dates.Index", "REPLACE_ID")
@Html.Hidden("Dates[REPLACE_ID].DateID", "REPLACE_ID")
@Html.TextBox("Dates[REPLACE_ID].StartDate", "")
</div>
<div class="grid-5-12">
@Html.TextBox("Dates[REPLACE_ID].EndDate", "")
</div>
</div>
Затем используется jQuery, который клонирует шаблон, предоставляет случайный идентификатор для использования для новой строки и добавляет теперь видимую клонированную строку к содержащему div выше:
jQuery для завершения процесса:
<script type="text/javascript">
function AddDatesRow() {
var tempIndex = Math.random().toString(36).substr(2, 5);
var template = $('#RowTemplate');
var insertRow = template.clone(false);
insertRow.find('input').each(function(){ //Run replace on each input
this.id = this.id.replace('REPLACE_ID', tempIndex);
this.name = this.name.replace('REPLACE_ID', tempIndex);
this.value = this.value.replace('REPLACE_ID', tempIndex);
});
insertRow.show();
$('#DatesBlock').append(insertRow.contents());
//Attach datepicker to new elements
$('input[name="Dates['+tempIndex+'].StartDate"]')
.datepicker({dateFormat: 'yy-mm-dd' });
$('input[name="Dates['+tempIndex+'].EndDate"]')
.datepicker({dateFormat: 'yy-mm-dd' });
}
</script>
Пример результата JSFiddle: http://jsfiddle.net/mdares/7JZh4/
Ответ 3
У меня есть динамический список, который выглядит так:
<ul id="okvedList" class="unstyled span8 editableList">
<li>
<input data-val="true" data-val-required="The Guid field is required." id="Okveds_0__Guid" name="Okveds[0].Guid" type="hidden" value="2627d99a-1fcd-438e-8109-5705dd0ac7bb">
--//--
</li>
поэтому, когда я добавляю или удаляю строку (элемент li), я должен переупорядочить элементы
this.reorderItems = function () {
var li = this.el_list.find('li');
for (var i = 0; i < li.length; i++) {
var inputs = $(li[i]).find('input');
$.each(inputs, function () {
var input = $(this);
var name = input.attr('name');
input.attr('name', name.replace(new RegExp("\\[.*\\]", 'gi'), '[' + i + ']'));
var id = input.attr('id');
input.attr('id', id.replace(new RegExp('_.*__', 'i'), '_' + i + '__'));
});
}
};
этот список помещается в простой Html.BeginFrom из clientide и как параметр List in action на serveride
Ответ 4
Я также сталкивался с аналогичной проблемой в прошлом, и я использую KnockoutJS для обработки такого сценария.
В принципе, нокаут отправляет коллекцию в строку JSON, и я десериализую их в своем контроллере.
За дополнительной информацией: http://learn.knockoutjs.com/#/?tutorial=collections
Ответ 5
У меня возникли небольшие проблемы, когда я пользуюсь браузером Chrome и нажимаю кнопку "Назад",
и я нахожу вход с типом = "скрытым", когда динамически установленные значения не обрабатываются должным образом браузером Chrome.
возможно, мы сможем изменить
<input type="hidden" name="Detes.Index" value="2016/01/06" />
to
<div style="display: none">
<input type="text" name="Detes.Index" value="2016/01/06" />
</div>
Форма больше информации:
Chrome не кэширует значения полей скрытой формы для использования в истории браузера
http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx/