Vous pouvez absolument faire cela, mais au début, les gens ont tendance à avoir des problèmes, alors je vais vous donner un exemple complet.
Pour commencer, nous allons définir une HomeController
qui a une action Index
qui retourne une List<EmployeeViewModel>
:
public class HomeController : Controller
{
public ActionResult Index()
{
// Assume this would really come from your database.
var employees = new List<EmployeeViewModel>()
{
new EmployeeViewModel { Id = 1, Name = "Employee 1" },
new EmployeeViewModel { Id = 2, Name = "Employee 2" },
new EmployeeViewModel { Id = 3, Name = "Employee 3" },
};
return View(employees);
}
[HttpPost]
public ActionResult Index(List<EmployeeViewModel> employees)
{
// Rest of action
}
}
La manière dont typique première approche de ce problème est de faire quelque chose comme ce qui suit dans index.cshtml
:
À première vue, cela semblerait fonctionner. Toutefois, si vous placez un point d'arrêt dans l'action POST et cliquez sur le bouton Envoyer, vous remarquerez que employees
est null
. La raison en est que la boucle foreach
génère HTML comme ce qui suit:
<input name="item.Id" type="number" value="1" />
<input name="item.Name" type="text" value="Employee 1" />
<input name="item.Id" type="number" value="2" />
<input name="item.Name" type="text" value="Employee 2" />
<input name="item.Id" type="number" value="3" />
<input name="item.Name" type="text" value="Employee 3" />
J'ai dépouillé quelques parties non pertinentes là, mais remarquez comment les name
attributs ont les mêmes valeurs pour chaque employé. Lorsque le classeur de modèle par défaut tente de construire la liste de données sur le serveur, il doit être capable de distinguer les différents employés, et, parce qu'il ne peut pas, il en résulte que la liste est null
. Pourquoi donc génère-t-il les mêmes valeurs? Il est pour cette raison:
@Html.EditorFor(x => item.Id)
@Html.EditorFor(x => item.Name)
Nous ne sommes pas passer une valeur d'index aux appels de méthode HtmlHelper
, de sorte que l'information ne permet pas au code HTML généré. Nous pouvons résoudre ce problème simplement en utilisant une boucle for
à la place:
for (int i = 0; i < Model.Count; i++)
{
@Html.EditorFor(x => Model[i].Id)
@Html.EditorFor(x => Model[i].Name)
}
Comme nous fournit maintenant un indice à chaque appel de méthode, le code HTML généré ne contient maintenant un index pour chaque employé:
<input name="[0].Id" type="number" value="1" />
<input name="[0].Name" type="text" value="Employee 1" />
<input name="[1].Id" type="number" value="2" />
<input name="[1].Name" type="text" value="Employee 2" />
<input name="[2].Id" type="number" value="3" />
<input name="[2].Name" type="text" value="Employee 3" />
Cela permet au classeur de modèle par défaut d'associer un Id
et un Name
à chaque EmployeeViewModel
, ce qui lui permet de construire correctement le type sur le serveur.
À ce stade, votre problème est résolu, cependant, il n'est pas recommandé d'utiliser une boucle for
si vous pouvez l'éviter, ce qui nous amène à des modèles d'éditeur. Les modèles d'éditeur sont des méthodes HtmlHelper
qui nous permettent de rendre un modèle personnalisé (c'est-à-dire une vue) pour un type donné. Alors laissez-moi vous montrer un exemple de la façon de le faire avec l'exemple de code ci-dessus.
Pour commencer, vous aurez besoin de faire ce qui suit:
- Créez un dossier
EditorTemplates
intérieur de votre dossier ~/Views/Home/
(le nom EditorTemplates
a une signification particulière dans MVC, il est donc important de préciser correctement) .
- Créez une vue
EmployeeViewModel.cshtml
à l'intérieur de ce dossier (encore une fois, le nom est important ici: il doit correspondre au nom du type pour lequel vous souhaitez créer un modèle personnalisé).
Une fois que vous avez fait cela, il devrait ressembler à ceci:
Maintenant, ouvrez EmployeeViewModel.cshtml
, et de mettre votre code de rendu de Index.cs
à l'intérieur de celui-ci:
@model EmployeeViewModel
<div class="row">
@Html.EditorFor(x => x.Id)
</div>
<div class="row">
@Html.EditorFor(x => x.Name)
</div>
Enfin, ouvrez index.cshtml
et remplacez-le par le suivant:
@model List<EmployeeViewModel>
@using (Html.BeginForm())
{
@Html.EditorFor(x => x)
<input type="submit" />
}
Html.EditorFor
et Html.DisplayFor
, sont à la fois assez intelligent pour reconnaître quand ils sont appelés pour une collection, donc ils cherchent un modèle personnalisé pour rendre le type de la collection (dans ce cas, EditorFor
recherchera un modèle d'éditeur pour EmployeeViewModel
). Comme nous avons fourni un modèle d'éditeur, non seulement il le rendra pour chaque élément de la collection, mais il générera également les indices corrects pour chacun de ces éléments, donnant au relieur du modèle toutes les informations dont il a besoin pour reconstruire cette collection sur le serveur. Le résultat final est que la liaison de modèle devient plus simple, et non seulement le code pour vos vues est plus simple, mais il est également divisé en fonction des types impliqués, ce qui rend vos vues plus faciles à utiliser, par opposition à vue géante qui fait tout.
Je devrais également mentionner que comme mon exemple utilise directement une collection, et rien de plus, vous pouvez effectivement remplacer @Html.EditorFor(x => x)
par @Html.EditorForModel()
. Je ne l'ai pas fait au départ car je ne voulais pas donner l'impression que les modèles sont appelés simplement en utilisant ce dernier.
Oh ouah! Merci beaucoup!! –
@HarporSydney De rien. :) –