Utiliser ViewData et implémenter des classes ViewModel
par Microsoft
Il s’agit de l’étape 6 d’un didacticiel gratuit sur l’application « NerdDinner » qui explique comment créer une application web petite mais complète à l’aide de ASP.NET MVC 1.
L’étape 6 montre comment activer la prise en charge des scénarios de modification de formulaire plus riches, et décrit également deux approches qui peuvent être utilisées pour passer des données des contrôleurs aux vues : ViewData et ViewModel.
Si vous utilisez ASP.NET MVC 3, nous vous recommandons de suivre les didacticiels Prise en main Avec MVC 3 ou MVC Music Store.
Étape 6 de NerdDinner : ViewData et ViewModel
Nous avons couvert un certain nombre de scénarios de publication de formulaires et expliqué comment implémenter la prise en charge de la création, de la mise à jour et de la suppression (CRUD). Nous allons maintenant aller plus loin dans l’implémentation de DinnersController et activer la prise en charge des scénarios de modification de formulaire plus riches. Dans ce cas, nous aborderons deux approches qui peuvent être utilisées pour passer des données des contrôleurs aux vues : ViewData et ViewModel.
Transmission de données de contrôleurs à View-Templates
L’une des caractéristiques de définition du modèle MVC est la stricte « séparation des préoccupations » qu’il permet d’appliquer entre les différents composants d’une application. Les modèles, les contrôleurs et les vues ont chacun des rôles et des responsabilités bien définis, et ils communiquent entre eux de manière bien définie. Cela permet de promouvoir la testabilité et la réutilisation du code.
Lorsqu’une classe Controller décide de restituer une réponse HTML à un client, elle est chargée de transmettre explicitement au modèle d’affichage toutes les données nécessaires au rendu de la réponse. Les modèles d’affichage ne doivent jamais effectuer de récupération de données ou de logique d’application et doivent se limiter à avoir uniquement du code de rendu qui est piloté par le modèle/les données qui lui sont transmises par le contrôleur.
À l’heure actuelle, les données de modèle transmises par notre classe DinnersController à nos modèles d’affichage sont simples et simples : une liste d’objets Dinner dans le cas d’Index(), et un seul objet Dinner dans le cas de Details(), Edit(), Create() et Delete(). À mesure que nous ajoutons d’autres fonctionnalités d’interface utilisateur à notre application, nous allons souvent devoir transmettre plus que ces données pour afficher des réponses HTML dans nos modèles d’affichage. Par exemple, nous pouvons changer le champ « Pays » dans nos vues Modifier et Créer de la zone de texte HTML en liste déroulante. Au lieu de coder en dur la liste déroulante des noms de pays et de régions dans le modèle d’affichage, nous pourrions la générer à partir d’une liste de pays et de régions pris en charge que nous remplissons dynamiquement. Nous aurons besoin d’un moyen de passer l’objet Dinner et la liste des pays et régions pris en charge de notre contrôleur à nos modèles d’affichage.
Examinons deux façons d’y parvenir.
Utilisation du dictionnaire ViewData
La classe de base du contrôleur expose une propriété de dictionnaire « ViewData » qui peut être utilisée pour passer des éléments de données supplémentaires des contrôleurs aux vues.
Par exemple, pour prendre en charge le scénario dans lequel nous voulons changer la zone de texte « Pays » dans notre vue Modifier d’une zone de texte HTML en liste déroulante, nous pouvons mettre à jour notre méthode d’action Edit() pour passer (en plus d’un objet Dinner) un objet SelectList qui peut être utilisé comme modèle d’une liste déroulante « Pays ».
//
// GET: /Dinners/Edit/5
[Authorize]
public ActionResult Edit(int id) {
Dinner dinner = dinnerRepository.GetDinner(id);
ViewData["Countries"] = new SelectList(PhoneValidator.AllCountries, dinner.Country);
return View(dinner);
}
Le constructeur de SelectList ci-dessus accepte une liste de pays et de régions pour remplir la liste déroulante avec, ainsi que la valeur actuellement sélectionnée.
Nous pouvons ensuite mettre à jour notre modèle de vue Edit.aspx pour utiliser la méthode d’assistance Html.DropDownList() au lieu de la méthode d’assistance Html.TextBox() que nous avons utilisée précédemment :
<%= Html.DropDownList("Country", ViewData["Countries"] as SelectList) %>
La méthode d’assistance Html.DropDownList() ci-dessus prend deux paramètres. La première est le nom de l’élément de formulaire HTML à générer. Le deuxième est le modèle « SelectList » que nous avons passé via le dictionnaire ViewData. Nous utilisons le mot clé C# « as » pour caster le type dans le dictionnaire en tant que SelectList.
Et maintenant, lorsque nous exécutons notre application et accédons à l’URL /Dinners/Edit/1 dans notre navigateur, nous voyons que notre interface utilisateur de modification a été mise à jour pour afficher une liste déroulante de pays et de régions au lieu d’une zone de texte :
Étant donné que nous restituons également le modèle d’affichage Modifier à partir de la méthode HTTP-POST Edit (dans les scénarios où des erreurs se produisent), nous voulons nous assurer que nous mettons également à jour cette méthode pour ajouter selectList à ViewData lorsque le modèle d’affichage est affiché dans des scénarios d’erreur :
//
// POST: /Dinners/Edit/5
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int id, FormCollection collection) {
Dinner dinner = dinnerRepository.GetDinner(id);
try {
UpdateModel(dinner);
dinnerRepository.Save();
return RedirectToAction("Details", new { id=dinner.DinnerID });
}
catch {
ModelState.AddModelErrors(dinner.GetRuleViolations());
ViewData["countries"] = new SelectList(PhoneValidator.AllCountries, dinner.Country);
return View(dinner);
}
}
Et maintenant, notre scénario d’édition DinnersController prend en charge un DropDownList.
Utilisation d’un modèle ViewModel
L’approche du dictionnaire ViewData a l’avantage d’être assez rapide et facile à implémenter. Certains développeurs n’aiment pas utiliser des dictionnaires basés sur des chaînes, cependant, car les fautes de frappe peuvent entraîner des erreurs qui ne seront pas interceptées au moment de la compilation. Le dictionnaire ViewData non typé nécessite également l’utilisation de l’opérateur « as » ou de la conversion lors de l’utilisation d’un langage fortement typé comme C# dans un modèle d’affichage.
Une autre approche que nous pourrions utiliser est souvent appelée modèle « ViewModel ». Lors de l’utilisation de ce modèle, nous créons des classes fortement typées qui sont optimisées pour nos scénarios d’affichage spécifiques et qui exposent des propriétés pour les valeurs dynamiques/le contenu requis par nos modèles d’affichage. Nos classes de contrôleur peuvent ensuite remplir et passer ces classes optimisées pour l’affichage à notre modèle d’affichage à utiliser. Cela permet la sécurité de type, la vérification au moment de la compilation et l’intellisense de l’éditeur dans les modèles d’affichage.
Par exemple, pour activer les scénarios de modification de formulaire de dîner, nous pouvons créer une classe « DinnerFormViewModel », comme ci-dessous, qui expose deux propriétés fortement typées : un objet Dinner et le modèle SelectList nécessaire pour remplir la liste déroulante « Pays » :
public class DinnerFormViewModel {
// Properties
public Dinner Dinner { get; private set; }
public SelectList Countries { get; private set; }
// Constructor
public DinnerFormViewModel(Dinner dinner) {
Dinner = dinner;
Countries = new SelectList(PhoneValidator.AllCountries, dinner.Country);
}
}
Nous pouvons ensuite mettre à jour notre méthode d’action Edit() pour créer le DinnerFormViewModel à l’aide de l’objet Dinner que nous récupérons à partir de notre dépôt, puis le passer à notre modèle d’affichage :
//
// GET: /Dinners/Edit/5
[Authorize]
public ActionResult Edit(int id) {
Dinner dinner = dinnerRepository.GetDinner(id);
return View(new DinnerFormViewModel(dinner));
}
Nous allons ensuite mettre à jour notre modèle d’affichage afin qu’il attende un objet « DinnerFormViewModel » au lieu d’un objet « Dinner » en modifiant l’attribut « hérite » en haut de la page edit.aspx comme suit :
Inherits="System.Web.Mvc.ViewPage<NerdDinner.Controllers.DinnerFormViewModel>
Une fois cette opération effectuée, l’intellisense de la propriété « Model » dans notre modèle d’affichage sera mis à jour pour refléter le modèle objet du type DinnerFormViewModel que nous transmettons :
Nous pouvons ensuite mettre à jour notre code d’affichage pour qu’il fonctionne. Notez ci-dessous comment nous ne modifions pas les noms des éléments d’entrée que nous créons (les éléments de formulaire seront toujours nommés « Title », « Country ») mais nous mettons à jour les méthodes HTML Helper pour récupérer les valeurs à l’aide de la classe DinnerFormViewModel :
<p>
<label for="Title">Dinner Title:</label>
<%= Html.TextBox("Title", Model.Dinner.Title) %>
<%=Html.ValidationMessage("Title", "*") %>
</p>
<p>
<label for="Country">Country:</label>
<%= Html.DropDownList("Country", Model.Countries) %>
<%=Html.ValidationMessage("Country", "*") %>
</p>
Nous allons également mettre à jour notre méthode Edit post pour utiliser la classe DinnerFormViewModel lors du rendu des erreurs :
//
// POST: /Dinners/Edit/5
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int id, FormCollection collection) {
Dinner dinner = dinnerRepository.GetDinner(id);
try {
UpdateModel(dinner);
dinnerRepository.Save();
return RedirectToAction("Details", new { id=dinner.DinnerID });
}
catch {
ModelState.AddModelErrors(dinner.GetRuleViolations());
return View(new DinnerFormViewModel(dinner));
}
}
Nous pouvons également mettre à jour nos méthodes d’action Create() pour réutiliser la même classe DinnerFormViewModel afin d’activer la liste déroulante « Pays » dans celles-ci. Voici l’implémentation HTTP-GET :
//
// GET: /Dinners/Create
public ActionResult Create() {
Dinner dinner = new Dinner() {
EventDate = DateTime.Now.AddDays(7)
};
return View(new DinnerFormViewModel(dinner));
}
Voici l’implémentation de la méthode HTTP-POST Create :
//
// POST: /Dinners/Create
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(Dinner dinner) {
if (ModelState.IsValid) {
try {
dinner.HostedBy = "SomeUser";
dinnerRepository.Add(dinner);
dinnerRepository.Save();
return RedirectToAction("Details", new { id=dinner.DinnerID });
}
catch {
ModelState.AddModelErrors(dinner.GetRuleViolations());
}
}
return View(new DinnerFormViewModel(dinner));
}
Et maintenant, nos écrans Modifier et Créer prennent en charge les listes déroulantes pour choisir le pays ou la région.
Classes ViewModel personnalisées
Dans le scénario ci-dessus, notre classe DinnerFormViewModel expose directement l’objet de modèle Dinner en tant que propriété, ainsi qu’une propriété de modèle SelectList de prise en charge. Cette approche fonctionne parfaitement pour les scénarios où l’interface utilisateur HTML que nous voulons créer dans notre modèle d’affichage correspond relativement étroitement à nos objets de modèle de domaine.
Pour les scénarios où ce n’est pas le cas, une option que vous pouvez utiliser consiste à créer une classe ViewModel de forme personnalisée dont le modèle objet est plus optimisé pour la consommation par la vue et qui peut sembler complètement différent de l’objet de modèle de domaine sous-jacent. Par exemple, il peut potentiellement exposer différents noms de propriétés et/ou propriétés d’agrégation collectées à partir de plusieurs objets de modèle.
Les classes ViewModel personnalisées peuvent être utilisées à la fois pour passer des données des contrôleurs aux vues à restituer, ainsi que pour aider à gérer les données de formulaire publiées dans la méthode d’action d’un contrôleur. Pour ce scénario ultérieur, la méthode d’action peut mettre à jour un objet ViewModel avec les données publiées dans le formulaire, puis utiliser le instance ViewModel pour mapper ou récupérer un objet de modèle de domaine réel.
Les classes ViewModel personnalisées peuvent offrir une grande flexibilité et sont un élément à examiner chaque fois que vous trouvez le code de rendu dans vos modèles d’affichage ou le code de publication de formulaire à l’intérieur de vos méthodes d’action qui commencent à devenir trop compliquées. Il s’agit souvent d’un signe que vos modèles de domaine ne correspondent pas correctement à l’interface utilisateur que vous générez, et qu’une classe ViewModel de forme personnalisée intermédiaire peut vous aider.
étape suivante
Voyons maintenant comment nous pouvons utiliser des pages partielles et master pour réutiliser et partager l’interface utilisateur au sein de notre application.