Comment fonctionne la liaison de modèle de liste MVC 4?
Si je veux qu'un ensemble d'entrées dans un formulaire se lie à un List
dans MVC 4, je sais que la convention de nommage suivante pour input
name
les attributs fonctionneront:
<input name="[0].Id" type="text" />
<input name="[1].Id" type="text" />
<input name="[2].Id" type="text" />
Mais je suis curieux de savoir à quel point le classeur modèle est indulgent. Par exemple, qu'en est-il des éléments suivants:
<input name="[0].Id" type="text" />
<input name="[3].Id" type="text" />
<input name="[8].Id" type="text" />
Comment le classeur de modèle gérerait-il cela? Se lierait-il à un List
de longueur 9 avec des valeurs nulles? Ou serait-il toujours lié à un List
de longueur 3? Ou serait-il étouffer tout à fait?
Pourquoi Je soins
Je veux implémenter un formulaire dynamique dans lequel l'utilisateur peut ajouter des lignes au formulaire, et peut également supprimer des lignes du formulaire. Donc, si un utilisateur supprime la ligne 2 sur 8 lignes totales, je veux savoir si je dois renuméroter toutes les entrées suivantes.
5 réponses
Il existe un format de fil spécifique à utiliser avec les collections. Ceci est discuté sur le blog de Scott Hanselman ici:
Une autre entrée de blog de Phil Haack en parle ici:
Http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx
Enfin, une entrée de blog qui fait exactement ce que vous voulez ici:
Http://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/
J'ai suivi cette approche liée dans les blogs ci - dessus et j'ai ajouté quelques détails qui pourraient être utiles à certains-d'autant plus que je voulais ajouter dynamiquement n'importe quel nombre de lignes mais je ne voulais pas utiliser AJAX pour le faire (je voulais que le formulaire ne soit soumis que dans le post). Je ne voulais pas non plus m'inquiéter du maintien des identifiants séquentiels. Je capturais une liste de dates de début et de fin:
Voir Le Modèle:
public class WhenViewModel : BaseViewModel {
public List<DateViewModel> Dates { get; set; }
//... Other properties
}
Date De Début / Fin Modèle De Vue:
public class DateViewModel {
public string DateID { get; set; }
public DateTime? StartDate { get; set; }
public DateTime? EndDate { get; set; }
}
Puis en utilisant dans la page (avec 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>
Comme le billet de blog lié au billet @ErikTheVikings ci-dessus le décrit, la collection est créée par l'élément caché répété: @Html.Hidden("Dates.Index", d.DateID)
pour chaque entrée de la collection sur la page.
Je voulais ajouter arbitrairement des lignes sans utiliser AJAX pour poster des données sur le serveur, ce que j'ai fait en créant un div caché contenant un modèle d'une" ligne " / élément dans la collection:
Modèle "Caché" ligne:
<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>
Ensuite utilisé jQuery qui clone le modèle, fournit un id aléatoire à utiliser pour une nouvelle ligne et ajoute la ligne clonée maintenant visible à la div contenant ci-dessus:
JQuery pour terminer le processus:
<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 exemple du résultat: http://jsfiddle.net/mdares/7JZh4/
J'ai une liste dynamique qui ressemble à ceci:
<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>
Donc, quand j'ajoute ou supprime la ligne (élément li), je dois réorganiser les éléments
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 + '__'));
});
}
};
Cette liste placée en Html simple.BeginFrom du côté client et comme la liste dans le paramètre d'action sur le côté serveur
J'ai également fait face à un problème similaire dans le passé, et j'utilise KnockoutJS pour gérer un tel scénario.
Fondamentalement, Knockout envoie la collection dans une chaîne JSON, et je les désérialise dans mon contrôleur.
Pour plus d'Informations: http://learn.knockoutjs.com/#/?tutorial=collections
J'ai eu peu de problème, quand j'utilise le navigateur Chrome et cliquez sur le bouton Précédent, et je trouve l'entrée avec type = "hidden" lorsque les valeurs définies dynamiquement n'ont pas été gérées correctement par le navigateur Chrome.
Peut-être que nous pouvons changer
<input type="hidden" name="Detes.Index" value="2016/01/06" />
À
<div style="display: none">
<input type="text" name="Detes.Index" value="2016/01/06" />
</div>
Formulaire plus d'infos: Chrome ne cache pas les valeurs de champ de formulaire masquées à utiliser dans l'historique du navigateur http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx/