web-dev-qa-db-fra.com

Formulaire MVC non capable de poster Liste d'objets

donc j'ai une application MVC Asp.net qui a des problèmes. J'ai essentiellement une vue qui contient un formulaire et son contenu est lié à une liste d'objets. Dans cette boucle, il charge les vues PartialView avec les éléments en boucle. Maintenant, tout fonctionne jusqu'à la soumission du formulaire. Lorsqu'il est soumis, le contrôleur reçoit une liste d'objets null. Le code ci-dessous illustre les problèmes.

Vue parente:

@model IEnumerable<PlanCompareViewModel>
@using (Html.BeginForm("ComparePlans", "Plans", FormMethod.Post, new { id = "compareForm" }))
{
<div>
    @foreach (var planVM in Model)
    {
        @Html.Partial("_partialView", planVM)
    }
</div>
}

_vue partielle:

@model PlanCompareViewModel
<div>
    @Html.HiddenFor(p => p.PlanID)
    @Html.HiddenFor(p => p.CurrentPlan)
    @Html.CheckBoxFor(p => p.ShouldCompare)
   <input type="submit" value="Compare"/>
</div>

Et ce sont les classes pour le code ci-dessus:

PlanViewModel:

public class PlansCompareViewModel
{

    public int PlanID { get; set; }
    public Plan CurrentPlan { get; set; }
    public bool ShouldCompare { get; set; }
    public PlansCompareViewModel(Plan plan)
    {
        ShouldCompare = false;
        PlanID = plan.PlanId;
        CurrentPlan = plan;
    }

    public PlansCompareViewModel()
    {
        // TODO: Complete member initialization
    }
    public static IEnumerable<PlansCompareViewModel> CreatePlansVM(IEnumerable<Plan> plans)
    {
        return plans.Select(p => new PlansCompareViewModel(p)).AsEnumerable();
    }
}

Manette:

public class PlansController : MyBaseController
{
    [HttpPost]
    public ActionResult ComparePlans(IEnumerable<PlanCompareViewModel> model)
    {
         //the model passed into here is NULL
    }
}

Et le problème réside dans l'action du contrôleur. Autant que je sache, il devrait publier une liste énumérable de PlanCompareViewModels, mais elle est nulle. Lors de l'inspection des données de poste envoyées, il envoie les paramètres corrects. Et si je changeais 'IEnumerable' en 'FormCollection', il contiendrait les valeurs correctes. Quelqu'un peut-il comprendre pourquoi le classeur ne crée pas le bon objet? Je peux contourner cela en utilisant JavaScript, mais cela va à l'encontre de l'objectif! Toute aide serait grandement appréciée!

44
Sonoilmedico

Votre modèle est null car la manière dont vous fournissez les entrées à votre formulaire signifie que le classeur de modèle ne permet pas de distinguer les éléments. En ce moment, ce code:

@foreach (var planVM in Model)
{
    @Html.Partial("_partialView", planVM)
}

ne fournit aucun type d'indice à ces éléments. Donc, il générerait à plusieurs reprises une sortie HTML comme ceci:

<input type="hidden" name="yourmodelprefix.PlanID" />
<input type="hidden" name="yourmodelprefix.CurrentPlan" />
<input type="checkbox" name="yourmodelprefix.ShouldCompare" />

Toutefois, lorsque vous souhaitez vous lier à une collection, vous devez nommer vos éléments de formulaire avec un index, tel que:

<input type="hidden" name="yourmodelprefix[0].PlanID" />
<input type="hidden" name="yourmodelprefix[0].CurrentPlan" />
<input type="checkbox" name="yourmodelprefix[0].ShouldCompare" />
<input type="hidden" name="yourmodelprefix[1].PlanID" />
<input type="hidden" name="yourmodelprefix[1].CurrentPlan" />
<input type="checkbox" name="yourmodelprefix[1].ShouldCompare" />

Cet index est ce qui permet au classeur de modèle d’associer les différentes données, ce qui lui permet de construire le bon modèle. Alors voici ce que je vous suggère de faire pour y remédier. Plutôt que de parcourir votre collection en utilisant une vue partielle, exploitez plutôt la puissance des modèles. Voici les étapes à suivre:

  1. Créez un dossier EditorTemplates dans le dossier actuel de votre vue (par exemple, si votre vue est Home\Index.cshtml, créez le dossier Home\EditorTemplates).
  2. Créez une vue fortement typée dans ce répertoire avec le nom correspondant à votre modèle. Dans votre cas, ce serait PlanCompareViewModel.cshtml.

Maintenant, tout ce que vous avez dans votre vue partielle veut aller dans ce modèle:

@model PlanCompareViewModel
<div>
    @Html.HiddenFor(p => p.PlanID)
    @Html.HiddenFor(p => p.CurrentPlan)
    @Html.CheckBoxFor(p => p.ShouldCompare)
   <input type="submit" value="Compare"/>
</div>

Enfin, votre vue parent est simplifiée à ceci:

@model IEnumerable<PlanCompareViewModel>
@using (Html.BeginForm("ComparePlans", "Plans", FormMethod.Post, new { id = "compareForm" }))
{
<div>
    @Html.EditorForModel()
</div>
}

DisplayTemplates et EditorTemplates sont suffisamment intelligents pour savoir quand ils traitent des collections. Cela signifie qu'ils généreront automatiquement les noms corrects, y compris les index, pour vos éléments de formulaire afin que vous puissiez correctement relier le modèle à une collection.

93
John H

Veuillez lire ceci: http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx
Vous devez définir des indicateurs pour les attributs "name" de vos éléments HTML, tels que planCompareViewModel[0].PlanId, planCompareViewModel[1].PlanId, Afin de permettre au classeur de les analyser dans IEnumerable.
Au lieu de @foreach (var planVM in Model), utilisez la boucle for et restituez les noms avec des index.

30
LINQ2Vodka