Modèle MVC Null lors de l’utilisation de la vue partielle

J’ai un contrôleur MVC où le modèle sur la méthode post revient toujours à zéro. Je ne sais pas si c’est parce que j’utilise une vue partielle dans le formulaire.

Avez-vous une idée de la raison pour laquelle le modèle n’est pas renvoyé au contrôleur?

Modèle

entrez la description de l'image ici

Chargement du modèle

public List GetStaticMeasures(int businessUnitID) { List groups = ctx.Groups .Include("Datapoints") .Where(w => w.BusinessUnitID.Equals(businessUnitID)) .OrderBy(o => o.SortOrder).ToList(); groups.ForEach(g => g.Datapoints = g.Datapoints.OrderBy(d => d.SortOrder).ToList()); return groups; } 

Manette

 public ActionResult Data() { ViewBag.Notification = ssortingng.Empty; if (User.IsInRole(@"xxx\yyyyyy")) { List dataGroups = ctx.GetStaticMeasures(10); return View(dataGroups); } else { throw new HttpException(403, "You do not have access to the data."); } } [HttpPost] [ValidateAntiForgeryToken] public ActionResult Data(List model) { ViewBag.Notification = ssortingng.Empty; if (User.IsInRole(@"xxx\yyyyyy")) { if (ModelState.IsValid) { ctx.SaveChanges(model); ViewBag.Notification = "Save Successful"; } } else { throw new HttpException(403, "You do not have access to save the data."); } return View(model); } 

Vue principale

 @model List 
@using (Html.BeginForm()) { @Html.AntiForgeryToken() @Html.ValidationSummary(true)
@foreach (var g in @Model) {

@g.Name

foreach (var d in g.Datapoints) { @Html.Partial("Measures", d) }
}
}

Vue partielle

 @model Jmp.StaticMeasures.Models.Datapoint @Html.HiddenFor(d => d.ID) @Html.HiddenFor(d => d.Name) @Html.HiddenFor(d => d.SortOrder) @Html.DisplayTextFor(d => d.Name) @Html.EditorFor(d => d.StaticValue) @Html.ValidationMessageFor(d => d.StaticValue) 

Rendu HTML montrant des identifiants consécutifs

entrez la description de l'image ici

Comme vous l’avez fait remarquer à juste titre, c’est parce que vous utilisez un partiel. Cela se produit car Html.Partial n’a aucune idée de son fonctionnement sur une collection. Par conséquent, il ne génère pas les noms de vos éléments de formulaire avec votre intention de lier une collection.

Cependant, la solution à votre cas semble être assez simple. Plutôt que d’utiliser Html.Partial , vous pouvez simplement changer votre partiel en un EditorTemplate et appeler Html.EditorFor sur ce modèle. Html.EditorFor est suffisamment intelligent pour savoir quand il gère une collection. Il invoquera votre modèle pour chaque élément de la collection, générant ainsi les noms corrects sur votre formulaire.

Donc, pour faire ce dont vous avez besoin, suivez ces étapes:

  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 ). Le nom est important car il suit une convention pour trouver des modèles.
  2. Placez votre vue partielle dans ce dossier. Vous pouvez également le placer dans le dossier Shared\EditorTemplates .
  3. Renommez votre vue partielle en Datapoint.cshtml (cela est important car les noms de modèles sont basés sur la convention du nom du type).

Maintenant, le code de vue pertinent devient:

 // Note: I removed @ from Model here. @foreach (var g in Model) { 

@g.Name

@Html.EditorFor(m => g.DataPoints)
}

Cela garantit la séparation de vos points de vue, comme vous l’aviez initialement prévu.

Mise à jour par commentaires

Bon, comme je l’ai mentionné ci-dessous, le problème est que le classeur de modèles n’a aucun moyen d’associer un DataPoint de données au bon Group . La solution simple consiste à changer le code de vue en ceci:

 for (int i = 0; i < Model.Count; i++) { 

@Model[i].Name

@Html.EditorFor(m => m[i].DataPoints)
}

Cela générera correctement les noms et devrait résoudre le problème de liaison du modèle.

Additif OP

Suite à la réponse de John, j’ai également inclus les propriétés manquantes sur la table des groupes en tant que HiddenFor, ce qui me rend le modèle de retour sur le poste.

 @for (int i = 0; i < Model.Count(); i++) { @Html.HiddenFor(t => Model[i].ID) @Html.HiddenFor(t => Model[i].BusinessUnitID) @Html.HiddenFor(t => Model[i].SortOrder) @Html.HiddenFor(t => Model[i].Name) 

@Model[i].Name

@Html.EditorFor(m => Model[i].Datapoints)
}

Mise à jour 2 – Solution plus propre

Mon conseil pour utiliser un EditorTemplate pour chaque DataPoint s’applique également à chaque Group . Plutôt que d’avoir besoin de la boucle for , saupoudrez à nouveau la logique dans la vue, vous pouvez éviter cela entièrement en configurant un EditorTemplate for Group . Les mêmes étapes s’appliquent comme ci-dessus en ce qui concerne l’endroit où placer le modèle.

Dans ce cas, le modèle serait Group.cshtml et se présenterait comme suit:

 @model Jmp.StaticMeasures.Models.Group 

@Model.Name

@Html.EditorFor(m => m.DataPoints)

Comme indiqué ci-dessus, cela invoquera le modèle pour chaque élément de la collection, qui générera également les index corrects pour chaque Group . Votre vue originale peut maintenant être simplifiée pour:

 @model List @using (Html.BeginForm()) { // Other markup @Html.EditorForModel(); } 

Le classeur ne peut pas être lié à une liste d’objects s’il est renvoyé comme ceci. Oui, votre problème est partiel. Vous devez spécifier un numéro dans votre formulaire pour les identifiants.

Faites quelque chose comme ça:

  // pseudocode @model List 
@using (Html.BeginForm()) { @Html.AntiForgeryToken() @Html.ValidationSummary(true)
for(int i; i@g.Name @Html.HiddenFor(d => Model[i].Id) @Html.HiddenFor(d => Model[i].Name) @Html.HiddenFor(d => Model[i].SortOrder) @Html.DisplayTextFor(d => Model[i].Name) @Html.EditorFor(d => Model[i].StaticValue) @Html.ValidationMessageFor(d => Model[i].StaticValue)
}
}

Voir plus de détails sur la liaison à une liste dans le blog de Haack

Vous obtenez un modèle null en raison de la façon dont le classeur modèle gère les collections.

Votre vue partielle rend ces entrées comme par exemple:

  ... 

Et répétez cela pour chaque entrée de votre List . Malheureusement, le classeur de modèles ne saura pas comment gérer cela et vous obtiendrez une valeur null.

La façon dont vos entrées doivent regarder est la suivante:

  ...  

Il ne peut y avoir de pause dans la numérotation. Une façon d’obtenir ceci est de réécrire la façon dont vous utilisez les méthodes Html.xxxFor, par exemple: itérer sur la liste et faire ceci:

 @Html.HiddenFor(d => Model[i].Id) 

Voici deux ressources qui expliquent cela en détail et fournissent d’autres exemples sur la manière de faire fonctionner le classeur de modèles avec des collections:

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

http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx/