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.

56
demandé sur Eric 2013-02-12 03:06:04

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:

Http://www.hanselman.com/blog/ASPNETWireFormatForModelBindingToArraysListsCollectionsDictionaries.aspx

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/

46
répondu Erik Funkenbusch 2013-02-12 04:51:56

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/

16
répondu Matthew 2014-02-06 23:14:15

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

3
répondu Evgeny Popov 2014-01-06 16:33:02

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

1
répondu andri 2013-02-12 04:43:24

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/

0
répondu thomas wu 2017-05-23 12:10:48