Parte 5: Modifica di moduli e modelli
di Jon Galloway
MVC Music Store è un'applicazione di esercitazione che introduce e spiega in modo dettagliato come usare ASP.NET MVC e Visual Studio per lo sviluppo Web.
MVC Music Store è un'implementazione di negozio di esempio leggera che vende album musicali online e implementa l'amministrazione del sito di base, l'accesso utente e la funzionalità del carrello acquisti.
Questa serie di esercitazioni illustra in dettaglio tutti i passaggi eseguiti per compilare l'applicazione di esempio MVC Music Store ASP.NET. La parte 5 illustra modifica moduli e templating.
Nel capitolo precedente sono stati caricati dati dal database e visualizzati. In questo capitolo si abiliterà anche la modifica dei dati.
Creazione di StoreManagerController
Si inizierà creando un nuovo controller denominato StoreManagerController. Per questo controller si sfruttano le funzionalità di Scaffolding disponibili nell'aggiornamento degli strumenti MVC 3 di ASP.NET. Impostare le opzioni per la finestra di dialogo Aggiungi controller, come illustrato di seguito.
Quando si fa clic sul pulsante Aggiungi, si noterà che il meccanismo di scaffolding MVC 3 ASP.NET MVC 3 esegue una buona quantità di lavoro:
- Crea la nuova variabile StoreManagerController con una variabile Entity Framework locale
- Aggiunge una cartella StoreManager alla cartella Views del progetto
- Aggiunge la visualizzazione Create.cshtml, Delete.cshtml, Details.cshtml, Edit.cshtml e Index.cshtml, fortemente tipizzata alla classe Album
La nuova classe controller StoreManager include azioni del controller CRUD (create, read, update, delete) che sanno come usare la classe modello Album e usare il contesto entity Framework per l'accesso al database.
Modifica di una visualizzazione Scaffolded
È importante ricordare che, mentre questo codice è stato generato per noi, è standard ASP.NET codice MVC, proprio come abbiamo scritto in questa esercitazione. È destinato a risparmiare tempo per scrivere codice del controller boilerplate e creare manualmente le visualizzazioni fortemente tipizzate, ma questo non è il tipo di codice generato che si potrebbe aver visto prefaced con avvisi dire nei commenti su come non modificare il codice. Si tratta del codice e si prevede di modificarlo.
Iniziamo quindi con una rapida modifica alla visualizzazione Indice StoreManager (/Views/StoreManager/Index.cshtml). Questa visualizzazione visualizzerà una tabella che elenca gli Album nel nostro archivio con i collegamenti Modifica/Dettagli/Eliminazione e include le proprietà pubbliche dell'album. Rimuoveremo il campo AlbumArtUrl, perché non è molto utile in questo display. Nella <sezione tabella> del codice di visualizzazione rimuovere gli <elementi th> e <td> che circondano i riferimenti albumArtUrl, come indicato dalle righe evidenziate di seguito:
<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>
Il codice di visualizzazione modificato verrà visualizzato come segue:
@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>
Una prima occhiata a Store Manager
Eseguire ora l'applicazione e passare a /StoreManager/. In questo modo viene visualizzato l'indice di Store Manager appena modificato, che mostra un elenco degli album nell'archivio con collegamenti a Modifica, Dettagli ed Elimina.
Facendo clic sul collegamento Modifica viene visualizzato un modulo di modifica con i campi per l'album, inclusi gli elenchi a discesa per Genere e Artista.
Fare clic sul collegamento "Torna all'elenco" nella parte inferiore, quindi fare clic sul collegamento Dettagli per un album. In questo modo vengono visualizzate le informazioni dettagliate per un singolo album.
Fare di nuovo clic sul collegamento Indietro all'elenco, quindi fare clic su un collegamento Elimina. Viene visualizzata una finestra di dialogo di conferma, che mostra i dettagli dell'album e chiede se si vuole eliminarla.
Facendo clic sul pulsante Elimina in basso si elimina l'album e si torna alla pagina Indice, che mostra l'album eliminato.
Non è stato fatto con Store Manager, ma è disponibile il controller di lavoro e il codice di visualizzazione per le operazioni CRUD da iniziare.
Esaminare il codice controller di Store Manager
Il controller di Store Manager contiene una buona quantità di codice. Andiamo attraverso questo dall'alto verso il basso. Il controller include alcuni spazi dei nomi standard per un controller MVC, nonché un riferimento allo spazio dei nomi Modelli. Il controller ha un'istanza privata di MusicStoreEntities, usata da ognuna delle azioni del controller per l'accesso ai dati.
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();
Azioni Store Manager Index and Details
La visualizzazione indice recupera un elenco di album, inclusi i riferimenti a Genere e Artista di ogni album, come illustrato in precedenza quando si lavora sul metodo Sfoglia dello Store. La visualizzazione Indice segue i riferimenti agli oggetti collegati in modo che possa visualizzare il nome Di genere e il nome dell'artista di ogni album, in modo che il controller sia efficiente ed esegue query per queste informazioni nella richiesta originale.
//
// GET: /StoreManager/
public ViewResult Index()
{
var albums = db.Albums.Include(a => a.Genre).Include(a => a.Artist);
return View(albums.ToList());
}
L'azione del controller Dettagli del controller StoreManager funziona esattamente come l'azione Dettagli controller store scritta in precedenza: esegue query per l'ALBUM usando il metodo Find() e quindi la restituisce alla visualizzazione.
//
// GET: /StoreManager/Details/5
public ViewResult Details(int id)
{
Album album = db.Albums.Find(id);
return View(album);
}
Metodi di azione Create
I metodi di azione Create sono un po' diversi da quelli che abbiamo visto finora, perché gestiscono l'input del modulo. Quando un utente visita prima /StoreManager/Create/ verrà visualizzato un modulo vuoto. Questa pagina HTML conterrà un <elemento modulo> che contiene elementi di input della casella di testo e a discesa in cui possono immettere i dettagli dell'album.
Dopo aver compilato i valori del modulo Album, l'utente può premere il pulsante "Salva" per inviare queste modifiche all'applicazione per salvare all'interno del database. Quando l'utente preme il pulsante "salva" il <modulo eseguirà un MESSAGGIO HTTP-POST all'URL /StoreManager/Create/ e invia i <> valori del modulo> come parte di HTTP-POST.
ASP.NET MVC consente di suddividere facilmente la logica di questi due scenari di chiamata URL consentendo di implementare due metodi di azione "Create" separati all'interno della classe StoreManagerController, uno per gestire la ricerca HTTP-GET iniziale al /StoreManager/Create/ URL e l'altro per gestire le modifiche inviate HTTP-POST.
Passaggio di informazioni a una visualizzazione tramite ViewBag
In questa esercitazione sono stati usati viewBag, ma non ne abbiamo parlato molto. ViewBag consente di passare informazioni alla visualizzazione senza usare un oggetto modello fortemente tipizzato. In questo caso, l'azione Modifica controller HTTP-GET deve passare sia un elenco di generi che artisti al modulo per popolare i menu a discesa e il modo più semplice per farlo è quello di restituirli come elementi ViewBag.
ViewBag è un oggetto dinamico, ovvero è possibile digitare ViewBag.Foo o ViewBag.YourNameHere senza scrivere codice per definire tali proprietà. In questo caso, il codice del controller usa ViewBag.GenreId e ViewBag.ArtistId in modo che i valori a discesa inviati con il modulo saranno GenereId e ArtistId, ovvero le proprietà Album che verranno impostate.
Questi valori a discesa vengono restituiti al modulo usando l'oggetto SelectList, creato solo per tale scopo. Questa operazione viene eseguita usando il codice simile al seguente:
ViewBag.GenreId = new SelectList(db.Genres, "GenreId", "Name");
Come si può vedere dal codice del metodo di azione, vengono usati tre parametri per creare questo oggetto:
- Verrà visualizzato l'elenco degli elementi dell'elenco a discesa. Si noti che questa non è solo una stringa: stiamo passando un elenco di generi.
- Il parametro successivo passato a SelectList è il valore selezionato. Questo modo in cui SelectList sa come pre-selezionare un elemento nell'elenco. Questa operazione sarà più semplice da comprendere quando esaminiamo il modulo Di modifica, che è piuttosto simile.
- Il parametro finale è la proprietà da visualizzare. In questo caso, questo indica che la proprietà Genre.Name è ciò che verrà visualizzato all'utente.
Con questo aspetto, quindi, l'azione HTTP-GET Create è piuttosto semplice: due SelectList vengono aggiunti alla ViewBag e non viene passato alcun oggetto modello al modulo (dal momento che non è ancora stato creato).
//
// GET: /StoreManager/Create
public ActionResult Create()
{
ViewBag.GenreId = new SelectList(db.Genres, "GenreId", "Name");
ViewBag.ArtistId = new SelectList(db.Artists, "ArtistId", "Name");
return View();
}
Helper HTML per visualizzare gli elenchi a discesa nella visualizzazione Crea
Poiché abbiamo parlato del modo in cui i valori a discesa vengono passati alla visualizzazione, esaminiamo rapidamente la visualizzazione per vedere come vengono visualizzati questi valori. Nel codice di visualizzazione (/Views/StoreManager/Create.cshtml), verrà visualizzata la chiamata seguente per visualizzare l'elenco a discesa Genere.
@Html.DropDownList("GenreId",
String.Empty)
Questo è noto come helper HTML: un metodo di utilità che esegue un'attività di visualizzazione comune. Gli helper HTML sono molto utili per mantenere conciso e leggibile il codice di visualizzazione. L'helper Html.DropDownList viene fornito da ASP.NET MVC, ma come vedremo più avanti è possibile creare i nostri helper per visualizzare il codice che verrà riutilizzato nell'applicazione.
La chiamata Html.DropDownList deve solo essere detto due elementi: dove ottenere l'elenco da visualizzare e quale valore (se presente) deve essere pre-selezionato. Il primo parametro, GenreId, indica a DropDownList di cercare un valore denominato GenreId nel modello o in ViewBag. Il secondo parametro viene usato per indicare il valore da visualizzare come inizialmente selezionato nell'elenco a discesa. Poiché questo modulo è un modulo Crea, non viene passato alcun valore e String.Empty non viene passato.
Gestione dei valori del modulo pubblicato
Come illustrato in precedenza, sono disponibili due metodi di azione associati a ogni modulo. Il primo gestisce la richiesta HTTP-GET e visualizza il modulo. Il secondo gestisce la richiesta HTTP-POST, che contiene i valori del modulo inviati. Si noti che l'azione del controller ha un attributo [HttpPost] che indica ASP.NET MVC che deve rispondere solo alle richieste 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);
}
Questa azione ha quattro responsabilità:
-
- Leggere i valori del modulo
-
- Verificare se i valori del modulo superano le regole di convalida
-
- Se l'invio del modulo è valido, salvare i dati e visualizzare l'elenco aggiornato
-
- Se l'invio del modulo non è valido, riprodurre il modulo con errori di convalida
Lettura dei valori del modulo con associazione modello
L'azione del controller elabora un invio di modulo che include i valori per GenreId e ArtistId (dall'elenco a discesa) e i valori delle caselle di testo per Title, Price e AlbumArtUrl. Anche se è possibile accedere direttamente ai valori dei moduli, un approccio migliore consiste nell'usare le funzionalità di associazione di modelli integrate in ASP.NET MVC. Quando un'azione del controller accetta un tipo di modello come parametro, ASP.NET MVC tenterà di popolare un oggetto di tale tipo usando gli input del modulo (nonché i valori di route e querystring). A tale scopo, cercare i valori i cui nomi corrispondono alle proprietà dell'oggetto modello, ad esempio quando si imposta il valore GenreId del nuovo oggetto Album, cerca un input con il nome GenreId. Quando si creano visualizzazioni usando i metodi standard in ASP.NET MVC, il rendering dei moduli verrà sempre eseguito usando nomi di proprietà come nomi di campo di input, in modo che i nomi dei campi corrispondano.
Convalida del modello
Il modello viene convalidato con una semplice chiamata a ModelState.IsValid. Non sono state ancora aggiunte regole di convalida alla classe Album. Questa operazione verrà eseguita in un po', quindi al momento questo controllo non ha molto da fare. È importante che questo controllo ModelStat.IsValid si adatti alle regole di convalida inserite nel modello, quindi le modifiche future alle regole di convalida non richiederanno aggiornamenti al codice azione del controller.
Salvataggio dei valori inviati
Se l'invio del modulo supera la convalida, è possibile salvare i valori nel database. Con Entity Framework è sufficiente aggiungere il modello all'insieme Albums e chiamare SaveChanges.
db.Albums.Add(album);
db.SaveChanges();
Entity Framework genera i comandi SQL appropriati per rendere persistente il valore. Dopo aver salvato i dati, viene eseguito il reindirizzamento all'elenco degli album in modo da poter visualizzare l'aggiornamento. Questa operazione viene eseguita restituendo RedirectToAction con il nome dell'azione del controller che si vuole visualizzare. In questo caso, questo è il metodo Index.
Visualizzazione di invii di moduli non validi con errori di convalida
Nel caso di input del modulo non valido, i valori dell'elenco a discesa vengono aggiunti a ViewBag (come nel caso HTTP-GET) e i valori del modello associato vengono passati nuovamente alla visualizzazione per la visualizzazione. Gli errori di convalida vengono visualizzati automaticamente usando l'helper @Html.ValidationMessageFor HTML.
Test del modulo di creazione
Per testare questa operazione, eseguire l'applicazione e passare a /StoreManager/Create/. Verrà visualizzato il modulo vuoto restituito dal metodo StoreController Create HTTP-GET.
Compilare alcuni valori e fare clic sul pulsante Crea per inviare il modulo.
Gestione delle modifiche
La coppia di azioni Modifica (HTTP-GET e HTTP-POST) è molto simile ai metodi di azione Create appena esaminati. Poiché lo scenario di modifica comporta l'uso di un album esistente, il metodo Edit HTTP-GET carica l'Album in base al parametro "id", passato tramite la route. Questo codice per il recupero di un album da AlbumId è identico a quello esaminato in precedenza nell'azione controller Dettagli. Come per il metodo Create/HTTP-GET, i valori dell'elenco a discesa vengono restituiti tramite ViewBag. In questo modo è possibile restituire un oggetto Album come oggetto modello alla visualizzazione (fortemente tipizzata alla classe Album) passando dati aggiuntivi (ad esempio un elenco di Generi) tramite 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'azione Modifica HTTP-POST è molto simile all'azione Crea HTTP-POST. L'unica differenza è che invece di aggiungere un nuovo album al db. Raccolta album, viene individuata l'istanza corrente dell'album usando db. Entry(album) e impostarne lo stato su Modificato. Ciò indica a Entity Framework di modificare un album esistente anziché crearne uno nuovo.
//
// 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);
}
È possibile testarlo eseguendo l'applicazione e passando a /StoreManger/, quindi facendo clic sul collegamento Modifica per un album.
Verrà visualizzato il modulo Edit visualizzato dal metodo Edit HTTP-GET. Compilare alcuni valori e fare clic sul pulsante Salva.
In questo modo viene pubblicato il modulo, salvato i valori e viene restituito all'elenco Album, che mostra che i valori sono stati aggiornati.
Gestione dell'eliminazione
L'eliminazione segue lo stesso modello di Modifica e Creazione, usando un'azione del controller per visualizzare il modulo di conferma e un'altra azione del controller per gestire l'invio del modulo.
L'azione del controller HTTP-GET Delete è esattamente la stessa dell'azione precedente del controller Dei dettagli di Store Manager.
//
// GET: /StoreManager/Delete/5
public ActionResult Delete(int id)
{
Album album = db.Albums.Find(id);
return View(album);
}
Viene visualizzato un modulo fortemente tipizzato in un tipo album, usando il modello Elimina contenuto visualizzazione.
Il modello Elimina mostra tutti i campi per il modello, ma è possibile semplificare il modello. Modificare il codice di visualizzazione in /Views/StoreManager/Delete.cshtml impostando quanto segue.
@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>
}
Verrà visualizzata una conferma di eliminazione semplificata.
Se si fa clic sul pulsante Elimina, il modulo viene inviato nuovamente al server, che esegue l'azione DeleteConfirmed.
//
// 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");
}
L'azione http-POST delete controller esegue le azioni seguenti:
-
- Carica l'album in base all'ID
-
- Elimina l'album e salva le modifiche
-
- Reindirizza all'indice, mostrando che l'album è stato rimosso dall'elenco
Per testarlo, eseguire l'applicazione e passare a /StoreManager. Selezionare un album dall'elenco e fare clic sul collegamento Elimina.
Verrà visualizzata la schermata di conferma Elimina.
Se si fa clic sul pulsante Elimina, l'album viene rimosso e si torna alla pagina Indice store Manager, che mostra che l'album è stato eliminato.
Uso di un helper HTML personalizzato per troncare il testo
Si è verificato un potenziale problema con la pagina indice di Store Manager. Le proprietà Titolo album e Nome artista possono essere sufficientemente lunghe da poter generare la formattazione della tabella. Verrà creato un helper HTML personalizzato per consentire di troncare facilmente queste e altre proprietà nelle visualizzazioni.
La sintassi di @helper Razor ha reso abbastanza semplice creare funzioni helper personalizzate da usare nelle visualizzazioni. Aprire la visualizzazione /Views/StoreManager/Index.cshtml e aggiungere il codice seguente direttamente dopo la @model riga.
@helper Truncate(string
input, int length)
{
if (input.Length <= length) {
@input
} else {
@input.Substring(0, length)<text>...</text>
}
}
Questo metodo helper accetta una stringa e una lunghezza massima da consentire. Se il testo fornito è più breve della lunghezza specificata, l'helper lo restituisce così come è. Se è più lungo, tronca il testo e ne esegue il rendering "..." per il resto.
Ora è possibile usare l'helper Truncate per assicurarsi che le proprietà Titolo album e Nome artista siano inferiori a 25 caratteri. Di seguito è riportato il codice di visualizzazione completo che usa il nuovo helper Truncate.
@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>
Ora, quando si sfoglia l'URL /StoreManager/, gli album e i titoli vengono mantenuti al di sotto delle lunghezze massime.
Nota: questo mostra il semplice caso di creazione e uso di un helper in una sola visualizzazione. Per altre informazioni sulla creazione di helper che è possibile usare in tutto il sito, vedere il post di blog: http://bit.ly/mvc3-helper-options