Partie 5 : Formulaires de modification et création de modèles
par Jon Galloway
Le magasin de musique MVC est un tutoriel qui présente et explique pas à pas comment utiliser ASP.NET MVC et Visual Studio pour le développement web.
Le magasin de musique MVC est une implémentation légère d’exemple de magasin qui vend des albums de musique en ligne et implémente l’administration de site de base, la connexion des utilisateurs et les fonctionnalités de panier d’achat.
Cette série de tutoriels détaille toutes les étapes à suivre pour générer l’exemple d’application ASP.NET magasin de musique MVC. La partie 5 traite de la modification des formulaires et de la création de modèles.
Dans le chapitre précédent, nous avons chargé des données à partir de notre base de données et les affichions. Dans ce chapitre, nous allons également activer la modification des données.
Création du StoreManagerController
Nous allons commencer par créer un contrôleur appelé StoreManagerController. Pour ce contrôleur, nous allons tirer parti des fonctionnalités de génération automatique disponibles dans la mise à jour des outils ASP.NET MVC 3. Définissez les options de la boîte de dialogue Ajouter un contrôleur, comme indiqué ci-dessous.
Lorsque vous cliquez sur le bouton Ajouter, vous voyez que le mécanisme de génération automatique ASP.NET MVC 3 effectue une bonne quantité de travail pour vous :
- Il crée le nouveau StoreManagerController avec une variable Entity Framework locale
- Il ajoute un dossier StoreManager au dossier Views du projet.
- Elle ajoute la vue Create.cshtml, Delete.cshtml, Details.cshtml, Edit.cshtml et Index.cshtml, fortement typée à la classe Album
La nouvelle classe de contrôleur StoreManager comprend des actions de contrôleur CRUD (créer, lire, mettre à jour, supprimer) qui savent comment utiliser la classe de modèle Album et utiliser notre contexte Entity Framework pour l’accès à la base de données.
Modification d’une vue générée automatiquement
Il est important de se rappeler que, bien que ce code ait été généré pour nous, il s’agit d’un code standard ASP.NET MVC, tout comme nous l’avons écrit tout au long de ce tutoriel. Il est destiné à vous faire gagner du temps à écrire du code de contrôleur réutilisable et à créer manuellement les vues fortement typées, mais ce n’est pas le type de code généré que vous avez peut-être vu précédé d’avertissements graves dans les commentaires sur la façon dont vous ne devez pas modifier le code. Il s’agit de votre code et vous devez le modifier.
Nous allons donc commencer par une modification rapide de l’affichage Index StoreManager (/Views/StoreManager/Index.cshtml). Cette vue affiche un tableau qui répertorie les albums de notre magasin avec des liens Modifier/Détails/Supprimer, et inclut les propriétés publiques de l’album. Nous allons supprimer le champ AlbumArtUrl, car il n’est pas très utile dans cet affichage. Dans <la section tableau> du code de la vue, supprimez les <éléments th> et <td> entourant les références AlbumArtUrl, comme indiqué par les lignes mises en surbrillance ci-dessous :
<table>
<tr>
<th>
Genre
</th>
<th>
Artist
</th>
<th>
Title
</th>
<th>
Price
</th>
<th>
AlbumArtUrl
</th>
<th></th>
</tr>
@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.Genre.Name)
</td>
<td>
@Html.DisplayFor(modelItem => item.Artist.Name)
</td>
<td>
@Html.DisplayFor(modelItem => item.Title)
</td>
<td>
@Html.DisplayFor(modelItem => item.Price)
</td>
<td>
@Html.DisplayFor(modelItem => item.AlbumArtUrl)
</td>
<td>
@Html.ActionLink("Edit", "Edit", new { id=item.AlbumId }) |
@Html.ActionLink("Details", "Details", new { id=item.AlbumId }) |
@Html.ActionLink("Delete", "Delete", new { id=item.AlbumId })
</td>
</tr>
}
</table>
Le code de vue modifié s’affiche comme suit :
@model IEnumerable<MvcMusicStore.Models.Album>
@{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<p>
@Html.ActionLink("Create
New", "Create")
</p>
<table>
<tr>
<th>
Genre
</th>
<th>
Artist
</th>
<th>
Title
</th>
<th>
Price
</th>
<th></th>
</tr>
@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.Genre.Name)
</td>
<td>
@Html.DisplayFor(modelItem => item.Artist.Name)
</td>
<td>
@Html.DisplayFor(modelItem => item.Title)
</td>
<td>
@Html.DisplayFor(modelItem => item.Price)
</td>
<td>
@Html.ActionLink("Edit", "Edit", new { id=item.AlbumId }) |
@Html.ActionLink("Details", "Details", new { id=item.AlbumId }) |
@Html.ActionLink("Delete", "Delete", new { id=item.AlbumId })
</td>
</tr>
}
</table>
Un premier aperçu du Gestionnaire de magasin
À présent, exécutez l’application et accédez à /StoreManager/. Cela affiche l’index du gestionnaire de magasin que nous venons de modifier, affichant une liste des albums dans le magasin avec des liens vers Modifier, Détails et Supprimer.
Le fait de cliquer sur le lien Modifier affiche un formulaire de modification avec des champs pour l’album, y compris les listes déroulantes Genre et Artiste.
Cliquez sur le lien « Retour à la liste » en bas, puis cliquez sur le lien Détails d’un album. Cela affiche les informations détaillées d’un album individuel.
À nouveau, cliquez sur le lien Retour à la liste, puis cliquez sur un lien Supprimer. Une boîte de dialogue de confirmation affiche les détails de l’album et demande si nous sommes sûrs de vouloir le supprimer.
Cliquez sur le bouton Supprimer en bas pour supprimer l’album et vous renvoyer à la page Index, qui affiche l’album supprimé.
Nous n’avons pas terminé avec le Gestionnaire de magasin, mais nous disposons d’un contrôleur et d’un code d’affichage de travail à partir duquel les opérations CRUD peuvent démarrer.
Examiner le code du contrôleur du Gestionnaire de magasin
Le contrôleur du Gestionnaire de magasin contient une bonne quantité de code. Passons en revue cela de haut en bas. Le contrôleur inclut des espaces de noms standard pour un contrôleur MVC, ainsi qu’une référence à notre espace de noms Models. Le contrôleur dispose d’une instance privée de MusicStoreEntities, utilisée par chacune des actions du contrôleur pour l’accès aux données.
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MvcMusicStore.Models;
namespace MvcMusicStore.Controllers
{
public class StoreManagerController : Controller
{
private MusicStoreEntities db = new MusicStoreEntities();
Actions d’index et de détails du gestionnaire de magasin
La vue d’index récupère une liste d’albums, y compris les informations de type et d’artiste référencées de chaque album, comme nous l’avons vu précédemment lors de l’utilisation de la méthode Store Browse. La vue Index suit les références aux objets liés afin qu’elle puisse afficher le nom de genre et le nom de l’artiste de chaque album, de sorte que le contrôleur est efficace et interroge ces informations dans la demande d’origine.
//
// GET: /StoreManager/
public ViewResult Index()
{
var albums = db.Albums.Include(a => a.Genre).Include(a => a.Artist);
return View(albums.ToList());
}
L’action du contrôleur Détails du contrôleur StoreManager fonctionne exactement comme l’action Stocker les détails du contrôleur que nous avons écrite précédemment : elle interroge l’Album par ID à l’aide de la méthode Find(), puis la renvoie à la vue.
//
// GET: /StoreManager/Details/5
public ViewResult Details(int id)
{
Album album = db.Albums.Find(id);
return View(album);
}
Méthodes d’action create
Les méthodes d’action Créer sont un peu différentes de celles que nous avons vues jusqu’à présent, car elles gèrent l’entrée de formulaire. Lorsqu’un utilisateur visite /StoreManager/Create/ pour la première fois, un formulaire vide s’affiche. Cette page HTML contient un <élément de formulaire> qui contient des éléments d’entrée de liste déroulante et de zone de texte dans lesquels ils peuvent entrer les détails de l’album.
Une fois que l’utilisateur a renseigné les valeurs du formulaire Album, il peut appuyer sur le bouton « Enregistrer » pour renvoyer ces modifications à notre application afin de les enregistrer dans la base de données. Lorsque l’utilisateur appuie sur le bouton « Enregistrer », le <formulaire> effectue une opération HTTP-POST à l’URL /StoreManager/Create/ et envoie les <valeurs du formulaire> dans le cadre de HTTP-POST.
ASP.NET MVC nous permet de fractionner facilement la logique de ces deux scénarios d’appel d’URL en nous permettant d’implémenter deux méthodes d’action « Créer » distinctes dans notre classe StoreManagerController : l’une pour gérer l’accès HTTP-GET initial à l’URL /StoreManager/Create/, et l’autre pour gérer le HTTP-POST des modifications envoyées.
Transmission d’informations à une vue à l’aide de ViewBag
Nous avons utilisé le ViewBag plus tôt dans ce tutoriel, mais nous n’en avons pas beaucoup parlé. ViewBag nous permet de transmettre des informations à la vue sans utiliser un objet de modèle fortement typé. Dans ce cas, notre action Modifier le contrôleur HTTP-GET doit passer une liste de genres et d’artistes au formulaire pour remplir les listes déroulantes, et la façon la plus simple de le faire consiste à les renvoyer en tant qu’éléments ViewBag.
ViewBag est un objet dynamique, ce qui signifie que vous pouvez taper ViewBag.Foo ou ViewBag.YourNameHere sans écrire de code pour définir ces propriétés. Dans ce cas, le code du contrôleur utilise ViewBag.GenreId et ViewBag.ArtistId afin que les valeurs déroulantes envoyées avec le formulaire soient GenreId et ArtistId, qui sont les propriétés Album qu’ils définissent.
Ces valeurs déroulantes sont retournées au formulaire à l’aide de l’objet SelectList, qui est créé uniquement à cet effet. Cette opération est effectuée à l’aide d’un code semblable à celui-ci :
ViewBag.GenreId = new SelectList(db.Genres, "GenreId", "Name");
Comme vous pouvez le voir dans le code de la méthode d’action, trois paramètres sont utilisés pour créer cet objet :
- Liste des éléments affichés dans la liste déroulante. Notez qu’il ne s’agit pas seulement d’une chaîne : nous transmettons une liste de genres.
- Le paramètre suivant passé à selectlist est la valeur sélectionnée. C’est ainsi que selectlist sait comment pré-sélectionner un élément dans la liste. Cela sera plus facile à comprendre lorsque nous examinons le formulaire Modifier, qui est assez similaire.
- Le paramètre final est la propriété à afficher. Dans ce cas, cela indique que la propriété Genre.Name est ce qui sera montré à l’utilisateur.
Dans cet esprit, l’action Http-GET Create est assez simple : deux SelectLists sont ajoutées au ViewBag et aucun objet de modèle n’est passé au formulaire (car il n’a pas encore été créé).
//
// GET: /StoreManager/Create
public ActionResult Create()
{
ViewBag.GenreId = new SelectList(db.Genres, "GenreId", "Name");
ViewBag.ArtistId = new SelectList(db.Artists, "ArtistId", "Name");
return View();
}
Helpers HTML pour afficher les listes déroulantes dans la vue Créer
Étant donné que nous avons parlé de la façon dont les valeurs déroulantes sont passées à la vue, examinons rapidement la vue pour voir comment ces valeurs sont affichées. Dans le code de l’affichage (/Views/StoreManager/Create.cshtml), vous verrez que l’appel suivant est effectué pour afficher la liste déroulante Genre.
@Html.DropDownList("GenreId",
String.Empty)
Il s’agit d’une méthode d’assistance HTML, une méthode utilitaire qui effectue une tâche d’affichage courante. Les helpers HTML sont très utiles pour garder notre code d’affichage concis et lisible. L’assistance Html.DropDownList est fournie par ASP.NET MVC, mais comme nous le verrons plus tard, il est possible de créer nos propres helpers pour afficher le code que nous réutiliserons dans notre application.
L’appel Html.DropDownList doit simplement être informé de deux choses : où obtenir la liste à afficher et quelle valeur (le cas échéant) doit être présélectionnée. Le premier paramètre, GenreId, indique à DropDownList de rechercher une valeur nommée GenreId dans le modèle ou ViewBag. Le deuxième paramètre est utilisé pour indiquer la valeur à afficher comme initialement sélectionnée dans la liste déroulante. Étant donné que ce formulaire est un formulaire Create, il n’y a aucune valeur à présélectionner et String.Empty est passé.
Gestion des valeurs de formulaire publié
Comme nous l’avons vu précédemment, deux méthodes d’action sont associées à chaque formulaire. Le premier gère la requête HTTP-GET et affiche le formulaire. La seconde gère la requête HTTP-POST, qui contient les valeurs de formulaire envoyées. Notez que l’action du contrôleur a un attribut [HttpPost] qui indique ASP.NET MVC qu’elle ne doit répondre qu’aux requêtes HTTP-POST.
//
// POST: /StoreManager/Create
[HttpPost]
public ActionResult Create(Album album)
{
if (ModelState.IsValid)
{
db.Albums.Add(album);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.GenreId = new SelectList(db.Genres, "GenreId", "Name", album.GenreId);
ViewBag.ArtistId = new SelectList(db.Artists, "ArtistId", "Name", album.ArtistId);
return View(album);
}
Cette action a quatre responsabilités :
-
- Lire les valeurs du formulaire
-
- Vérifier si les valeurs de formulaire réussissent des règles de validation
-
- Si l’envoi du formulaire est valide, enregistrez les données et affichez la liste mise à jour
-
- Si l’envoi du formulaire n’est pas valide, réafficher le formulaire avec des erreurs de validation
Lecture des valeurs de formulaire avec une liaison de modèle
L’action du contrôleur traite une soumission de formulaire qui inclut des valeurs pour GenreId et ArtistId (dans la liste déroulante) et des valeurs de zone de texte pour Title, Price et AlbumArtUrl. Bien qu’il soit possible d’accéder directement aux valeurs de formulaire, une meilleure approche consiste à utiliser les fonctionnalités de liaison de modèle intégrées à ASP.NET MVC. Lorsqu’une action de contrôleur prend un type de modèle en tant que paramètre, ASP.NET MVC tente de remplir un objet de ce type à l’aide d’entrées de formulaire (ainsi que de valeurs de route et de chaîne de requête). Pour ce faire, il recherche des valeurs dont les noms correspondent aux propriétés de l’objet de modèle. Par exemple, lors de la définition de la valeur GenreId du nouvel objet Album, il recherche une entrée portant le nom GenreId. Lorsque vous créez des vues à l’aide des méthodes standard dans ASP.NET MVC, les formulaires sont toujours affichés à l’aide de noms de propriétés comme noms de champs d’entrée. Les noms des champs correspondent donc simplement.
Validation du modèle
Le modèle est validé avec un simple appel à ModelState.IsValid. Nous n’avons pas encore ajouté de règles de validation à notre classe Album - nous allons le faire dans un peu - donc pour l’instant, cette case activée n’a pas grand-chose à faire. Ce qui est important, c’est que cette case activée ModelStat.IsValid s’adapte aux règles de validation que nous avons placées sur notre modèle, de sorte que les futures modifications apportées aux règles de validation ne nécessitent aucune mise à jour du code d’action du contrôleur.
Enregistrement des valeurs envoyées
Si l’envoi du formulaire réussit la validation, il est temps d’enregistrer les valeurs dans la base de données. Avec Entity Framework, il suffit d’ajouter le modèle à la collection Albums et d’appeler SaveChanges.
db.Albums.Add(album);
db.SaveChanges();
Entity Framework génère les commandes SQL appropriées pour conserver la valeur. Après avoir enregistré les données, nous redirigeons vers la liste des albums afin de voir notre mise à jour. Pour ce faire, renvoyez RedirectToAction avec le nom de l’action de contrôleur que nous voulons afficher. Dans ce cas, il s’agit de la méthode Index.
Affichage des soumissions de formulaires non valides avec des erreurs de validation
En cas d’entrée de formulaire non valide, les valeurs déroulantes sont ajoutées au ViewBag (comme dans le cas HTTP-GET) et les valeurs du modèle lié sont renvoyées à la vue pour affichage. Les erreurs de validation s’affichent automatiquement à l’aide de HTML @Html.ValidationMessageFor Helper.
Test du formulaire de création
Pour tester ce problème, exécutez l’application et accédez à /StoreManager/Create/ : le formulaire vide qui a été retourné par la méthode StoreController Create HTTP-GET s’affiche.
Renseignez certaines valeurs et cliquez sur le bouton Créer pour envoyer le formulaire.
Gestion des modifications
La paire d’actions Modifier (HTTP-GET et HTTP-POST) est très similaire aux méthodes d’action Créer que nous venons d’examiner. Étant donné que le scénario d’édition implique l’utilisation d’un album existant, la méthode Edit HTTP-GET charge l’album en fonction du paramètre « id », transmis via l’itinéraire. Ce code pour la récupération d’un album par AlbumId est le même que celui que nous avons examiné précédemment dans l’action du contrôleur Détails. Comme avec la méthode Create/HTTP-GET, les valeurs déroulantes sont retournées via viewbag. Cela nous permet de retourner un Album en tant qu’objet de modèle à la vue (qui est fortement typée dans la classe Album) tout en transmettant des données supplémentaires (par exemple, une liste de genres) via le ViewBag.
//
// GET: /StoreManager/Edit/5
public ActionResult Edit(int id)
{
Album album = db.Albums.Find(id);
ViewBag.GenreId = new SelectList(db.Genres, "GenreId", "Name", album.GenreId);
ViewBag.ArtistId = new SelectList(db.Artists, "ArtistId", "Name", album.ArtistId);
return View(album);
}
L’action Modifier HTTP-POST est très similaire à l’action Créer HTTP-POST. La seule différence est qu’au lieu d’ajouter un nouvel album à la base de données. Collection Albums, nous trouvons la instance actuelle de l’album à l’aide de la base de données. Entry(album) et en définissant son état sur Modifié. Cela indique à Entity Framework que nous modifions un album existant au lieu d’en créer un.
//
// POST: /StoreManager/Edit/5
[HttpPost]
public ActionResult Edit(Album album)
{
if (ModelState.IsValid)
{
db.Entry(album).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.GenreId = new SelectList(db.Genres, "GenreId", "Name", album.GenreId);
ViewBag.ArtistId = new SelectList(db.Artists, "ArtistId", "Name", album.ArtistId);
return View(album);
}
Nous pouvons tester cela en exécutant l’application et en accédant à /StoreManger/, puis en cliquant sur le lien Modifier pour un album.
Cela affiche le formulaire Modifier affiché par la méthode Edit HTTP-GET. Renseignez certaines valeurs et cliquez sur le bouton Enregistrer.
Cette opération publie le formulaire, enregistre les valeurs et nous renvoie à la liste Albums, indiquant que les valeurs ont été mises à jour.
Gestion de la suppression
La suppression suit le même modèle que Modifier et Créer, en utilisant une action de contrôleur pour afficher le formulaire de confirmation et une autre action de contrôleur pour gérer l’envoi du formulaire.
L’action Supprimer le contrôleur HTTP-GET est exactement la même que l’action précédente du contrôleur Store Manager Details.
//
// GET: /StoreManager/Delete/5
public ActionResult Delete(int id)
{
Album album = db.Albums.Find(id);
return View(album);
}
Nous affichons un formulaire fortement typé sur un type Album, à l’aide du modèle de contenu Supprimer l’affichage.
Le modèle Supprimer affiche tous les champs du modèle, mais nous pouvons simplifier cela un peu. Remplacez le code d’affichage dans /Views/StoreManager/Delete.cshtml par ce qui suit.
@model MvcMusicStore.Models.Album
@{
ViewBag.Title = "Delete";
}
<h2>Delete Confirmation</h2>
<p>Are you sure you want to delete the album titled
<strong>@Model.Title</strong>?
</p>
@using (Html.BeginForm()) {
<p>
<input type="submit" value="Delete" />
</p>
<p>
@Html.ActionLink("Back to
List", "Index")
</p>
}
Cette opération affiche une confirmation de suppression simplifiée.
Cliquer sur le bouton Supprimer entraîne la publication du formulaire sur le serveur, qui exécute l’action SupprimerConfirmed.
//
// POST: /StoreManager/Delete/5
[HttpPost, ActionName("Delete")]
public ActionResult DeleteConfirmed(int id)
{
Album album = db.Albums.Find(id);
db.Albums.Remove(album);
db.SaveChanges();
return RedirectToAction("Index");
}
Notre action de suppression du contrôleur HTTP-POST effectue les actions suivantes :
-
- Charge l’album par ID
-
- Supprime l’album et enregistre les modifications
-
- Redirige vers l’index, montrant que l’album a été supprimé de la liste
Pour tester cela, exécutez l’application et accédez à /StoreManager. Sélectionnez un album dans la liste, puis cliquez sur le lien Supprimer.
L’écran de confirmation Supprimer s’affiche.
Le fait de cliquer sur le bouton Supprimer supprime l’album et nous renvoie à la page Index du Gestionnaire de magasin, qui indique que l’album a été supprimé.
Utilisation d’une assistance HTML personnalisée pour tronquer du texte
Nous avons un problème potentiel avec notre page d’index du Gestionnaire de magasin. Nos propriétés Titre de l’album et Nom de l’artiste peuvent être suffisamment longues pour qu’elles puissent désactiver la mise en forme de notre tableau. Nous allons créer une assistance HTML personnalisée pour nous permettre de tronquer facilement ces propriétés et d’autres propriétés dans nos vues.
La syntaxe de @helper Razor a facilité la création de vos propres fonctions d’assistance à utiliser dans vos vues. Ouvrez la vue /Views/StoreManager/Index.cshtml et ajoutez le code suivant directement après la @model ligne.
@helper Truncate(string
input, int length)
{
if (input.Length <= length) {
@input
} else {
@input.Substring(0, length)<text>...</text>
}
}
Cette méthode d’assistance prend une chaîne et une longueur maximale à autoriser. Si le texte fourni est plus court que la longueur spécifiée, l’assistance le génère tel qu’il est. S’il est plus long, il tronque le texte et affiche « ... » pour le reste.
Nous pouvons maintenant utiliser notre assistance Truncate pour nous assurer que les propriétés Titre de l’album et Nom de l’artiste sont inférieures à 25 caractères. Le code d’affichage complet utilisant notre nouvelle assistance Truncate apparaît ci-dessous.
@model IEnumerable<MvcMusicStore.Models.Album>
@helper Truncate(string input, int length)
{
if (input.Length <= length) {
@input
} else {
@input.Substring(0, length)<text>...</text>
}
}
@{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<p>
@Html.ActionLink("Create
New", "Create")
</p>
<table>
<tr>
<th>
Genre
</th>
<th>
Artist
</th>
<th>
Title
</th>
<th>
Price
</th>
<th></th>
</tr>
@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.Genre.Name)
</td>
<td>
@Truncate(item.Artist.Name, 25)
</td>
<td>
@Truncate(item.Title, 25)
</td>
<td>
@Html.DisplayFor(modelItem => item.Price)
</td>
<td>
@Html.ActionLink("Edit", "Edit", new { id=item.AlbumId }) |
@Html.ActionLink("Details", "Details", new { id=item.AlbumId }) |
@Html.ActionLink("Delete", "Delete", new { id=item.AlbumId })
</td>
</tr>
}
</table>
Maintenant, lorsque nous parcourons l’URL /StoreManager/, les albums et les titres sont conservés en dessous de nos longueurs maximales.
Remarque : cela montre le cas simple de la création et de l’utilisation d’une assistance dans une seule vue. Pour en savoir plus sur la création d’assistances que vous pouvez utiliser dans l’ensemble de votre site, consultez mon billet de blog : http://bit.ly/mvc3-helper-options