Badanie sposobu tworzenia szkieletu pomocnika DropDownList przez wzorzec ASP.NET MVC
Autor: Rick Anderson
W Eksplorator rozwiązań kliknij prawym przyciskiem myszy folder Controllers, a następnie wybierz polecenie Dodaj kontroler. Nadaj kontrolerowi nazwę StoreManagerController. Ustaw opcje okna dialogowego Dodawanie kontrolera , jak pokazano na poniższej ilustracji.
Edytuj widok StoreManager\Index.cshtml i usuń polecenie AlbumArtUrl
. Usunięcie AlbumArtUrl
spowoduje, że prezentacja będzie bardziej czytelna. Ukończony kod jest pokazany poniżej.
@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>
Otwórz plik Controllers\StoreManagerController.cs i znajdź metodę Index
. Dodaj klauzulę , OrderBy
aby albumy zostały posortowane według ceny. Pełny kod jest pokazany poniżej.
public ViewResult Index()
{
var albums = db.Albums.Include(a => a.Genre).Include(a => a.Artist)
.OrderBy(a => a.Price);
return View(albums.ToList());
}
Sortowanie według ceny ułatwi testowanie zmian w bazie danych. Podczas testowania metod edycji i tworzenia można użyć niskiej ceny, aby zapisane dane pojawiły się jako pierwsze.
Otwórz plik StoreManager\Edit.cshtml. Dodaj następujący wiersz tuż po tagu legendy.
@Html.HiddenFor(model => model.AlbumId)
Poniższy kod przedstawia kontekst tej zmiany:
@using (Html.BeginForm()) {
@Html.ValidationSummary(true)
<fieldset>
<legend>Album</legend>
@Html.HiddenFor(model => model.AlbumId)
<div class="editor-label">
@Html.LabelFor(model => model.GenreId, "Genre")
</div>
<div class="editor-field">
@Html.DropDownList("GenreId", String.Empty)
@Html.ValidationMessageFor(model => model.GenreId)
</div>
<!-- Items removed for brevity. -->
}
Wymagane AlbumId
jest wprowadzenie zmian w rekordzie albumu.
Naciśnij klawisze CTRL+F5, aby uruchomić aplikację. Wybierz link Administrator, a następnie wybierz link Utwórz nowy, aby utworzyć nowy album. Sprawdź, czy informacje o albumie zostały zapisane. Edytuj album i sprawdź, czy wprowadzone zmiany są utrwalane.
Schemat albumu
Kontroler StoreManager
utworzony przez mechanizm tworzenia szkieletów MVC umożliwia dostęp CRUD (Create, Read, Update, Delete) do albumów w bazie danych sklepu muzycznego. Schemat informacji o albumie jest pokazany poniżej:
Tabela Albums
nie przechowuje gatunku albumu i opisu, przechowuje klucz obcy do Genres
tabeli. Tabela Genres
zawiera nazwę gatunku i opis. Albums
Podobnie tabela nie zawiera nazwy artystów albumów, ale klucza obcego do Artists
tabeli. Tabela Artists
zawiera nazwę artysty. Jeśli zbadasz dane w Albums
tabeli, zobaczysz, że każdy wiersz zawiera klucz Genres
obcy do tabeli i klucz obcy w Artists
tabeli. Na poniższej ilustracji Albums
przedstawiono niektóre dane tabeli z tabeli.
The HTML Select Tag
Element HTML <select>
(utworzony przez pomocnik HTML DropDownList ) służy do wyświetlania pełnej listy wartości (takich jak lista gatunków). W przypadku formularzy edycji, gdy bieżąca wartość jest znana, lista wyboru może wyświetlić bieżącą wartość. Widzieliśmy to wcześniej, gdy ustawiliśmy wybraną wartość na Comedy. Lista wyboru jest idealna do wyświetlania danych kategorii lub klucza obcego. Element <select>
klucza obcego gatunku wyświetla listę możliwych nazw gatunków, ale po zapisaniu formularza właściwość Gatunek zostanie zaktualizowana o wartość klucza obcego gatunku, a nie wyświetlaną nazwę gatunku. Na poniższej ilustracji wybrany gatunek to Disco , a artysta jest Donna Summer.
Badanie kodu szkieletowego ASP.NET MVC
Otwórz plik Controllers\StoreManagerController.cs i znajdź metodę HTTP GET Create
.
public ActionResult Create()
{
ViewBag.GenreId = new SelectList(db.Genres, "GenreId", "Name");
ViewBag.ArtistId = new SelectList(db.Artists, "ArtistId", "Name");
return View();
}
Metoda Create
dodaje dwa obiekty SelectList do obiektu , które zawierają informacje o gatunku ViewBag
, i jeden zawierający informacje o wykonawcy. Powyższe przeciążenie konstruktora SelectList przyjmuje trzy argumenty:
public SelectList(
IEnumerable items,
string dataValueField,
string dataTextField
)
- items: element IEnumerable zawierający elementy na liście. W powyższym przykładzie lista gatunków zwracanych przez
db.Genres
element . - dataValueField: nazwa właściwości na liście IEnumerable zawierająca wartość klucza. W powyższym
GenreId
przykładzie iArtistId
. - dataTextField: nazwa właściwości na liście IEnumerable zawierająca informacje do wyświetlenia. W tabeli
name
artystów i gatunku używane jest pole.
Otwórz plik Views\StoreManager\Create.cshtml i sprawdź Html.DropDownList
znacznik pomocnika dla pola gatunku.
@model MvcMusicStore.Models.Album
@* Markup removed for clarity.*@
@Html.DropDownList("GenreId", String.Empty)
Pierwszy wiersz pokazuje, że widok tworzenia przyjmuje Album
model. W metodzie pokazanej Create
powyżej nie przekazano żadnego modelu, więc widok pobiera model o wartości nullAlbum
. W tym momencie tworzymy nowy album, więc nie mamy dla niego żadnych Album
danych.
Przeciążenie Html.DropDownList pokazane powyżej przyjmuje nazwę pola, które ma być powiązane z modelem. Używa również tej nazwy do wyszukiwania obiektu ViewBag zawierającego obiekt SelectList . Za pomocą tego przeciążenia należy nazwać obiekt GenreId
ViewBag SelectList . Drugi parametr (String.Empty
) to tekst do wyświetlenia, gdy nie wybrano żadnego elementu. Jest to dokładnie to, czego chcemy podczas tworzenia nowego albumu. Jeśli drugi parametr został usunięty i użyto następującego kodu:
@Html.DropDownList("GenreId")
Lista wyboru jest domyślnie ustawiona na pierwszy element lub Rock w naszym przykładzie.
HTTP POST Create
Badanie metody.
//
// 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);
}
To przeciążenie Create
metody przyjmuje album
obiekt utworzony przez system powiązania modelu MVC ASP.NET z opublikowanych wartości formularza. Po przesłaniu nowego albumu, jeśli stan modelu jest prawidłowy i nie ma błędów bazy danych, nowy album zostanie dodany do bazy danych. Na poniższej ilustracji przedstawiono tworzenie nowego albumu.
Za pomocą narzędzia fiddler można sprawdzić opublikowane wartości formularzy, które ASP.NET powiązania modelu MVC używane do tworzenia obiektu albumu.
.
Refaktoryzacja tworzenia elementu ViewBag SelectList
Edit
Zarówno metody, jak i HTTP POST Create
metoda mają identyczny kod, aby skonfigurować selectList w ViewBag. W duchu DRY refaktoryzujemy ten kod. Użyjemy tego refaktoryzowanego kodu później.
Utwórz nową metodę, aby dodać gatunek i artystę SelectList do elementu ViewBag.
private void SetGenreArtistViewBag(int? GenreID = null, int? ArtistID = null) {
if (GenreID == null)
ViewBag.GenreId = new SelectList(db.Genres, "GenreId", "Name");
else
ViewBag.GenreId = new SelectList(db.Genres.ToArray(), "GenreId", "Name", GenreID);
if (ArtistID == null)
ViewBag.ArtistId = new SelectList(db.Artists, "ArtistId", "Name");
else
ViewBag.ArtistId = new SelectList(db.Artists, "ArtistId", "Name", ArtistID);
}
Zastąp dwa wiersze ustawieniem ViewBag
w każdej z Create
metod i Edit
wywołaniem SetGenreArtistViewBag
metody . Ukończony kod jest pokazany poniżej.
//
// GET: /StoreManager/Create
public ActionResult Create() {
SetGenreArtistViewBag();
return View();
}
//
// POST: /StoreManager/Create
[HttpPost]
public ActionResult Create(Album album) {
if (ModelState.IsValid) {
db.Albums.Add(album);
db.SaveChanges();
return RedirectToAction("Index");
}
SetGenreArtistViewBag(album.GenreId, album.ArtistId);
return View(album);
}
//
// GET: /StoreManager/Edit/5
public ActionResult Edit(int id) {
Album album = db.Albums.Find(id);
SetGenreArtistViewBag(album.GenreId, album.ArtistId);
return View(album);
}
//
// POST: /StoreManager/Edit/5
[HttpPost]
public ActionResult Edit(Album album) {
if (ModelState.IsValid) {
db.Entry(album).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
SetGenreArtistViewBag(album.GenreId, album.ArtistId);
return View(album);
}
Utwórz nowy album i edytuj album, aby sprawdzić, czy zmiany działają.
Jawne przekazywanie listy SelectList do listy rozwijanej
Tworzenie i edytowanie widoków utworzonych przez szkielet ASP.NET MVC używa następującego przeciążenia DropDownList :
public static MvcHtmlString DropDownList(
this HtmlHelper htmlHelper,
string name, // The name of the ViewModel property to bind.
string optionLabel // The string added to the top of the list
// typically String.Empty or "Select a Genre"
)
Poniżej DropDownList
przedstawiono znaczniki dla widoku tworzenia.
@Html.DropDownList("GenreId", String.Empty)
ViewBag
Ponieważ właściwość obiektu SelectList
ma nazwę GenreId
, pomocnik DropDownList użyjeGenreId
kontrolki SelectList w obiekcie ViewBag. W poniższym przeciążeniu SelectList
DropDownList parametr jest jawnie przekazywany.
public static MvcHtmlString DropDownList(
this HtmlHelper htmlHelper,
string name, // The name of the ViewModel property to bind.
IEnumerable selectList // The SelectList
)
Otwórz plik Views\StoreManager\Edit.cshtml i zmień wywołanie DropDownList, aby jawnie przekazać element SelectList przy użyciu przeciążenia powyżej. Zrób to dla kategorii Gatunek. Ukończony kod jest pokazany poniżej:
@Html.DropDownList("GenreId", ViewBag.GenreId as SelectList)
Uruchom aplikację i kliknij link Administrator , a następnie przejdź do albumu Jazz i wybierz link Edytuj .
Zamiast pokazywać Jazz jako aktualnie wybrany gatunek, rock jest wyświetlany. Gdy argument ciągu (właściwość do powiązania) i obiekt SelectList mają taką samą nazwę, wybrana wartość nie jest używana. Jeśli nie jest podana żadna wybrana wartość, przeglądarki są domyślne dla pierwszego elementu w elemecie SelectList (czyli Rock w powyższym przykładzie). Jest to znane ograniczenie pomocnika DropDownList .
Otwórz plik Controllers\StoreManagerController.cs i zmień nazwy obiektów SelectList na Genres
i Artists
. Ukończony kod jest pokazany poniżej:
private void SetGenreArtistViewBag(int? GenreID = null, int? ArtistID = null) {
if (GenreID == null)
ViewBag.Genres = new SelectList(db.Genres, "GenreId", "Name");
else
ViewBag.Genres = new SelectList(db.Genres.ToArray(), "GenreId", "Name", GenreID);
if (ArtistID == null)
ViewBag.Artists = new SelectList(db.Artists, "ArtistId", "Name");
else
ViewBag.Artists = new SelectList(db.Artists, "ArtistId", "Name", ArtistID);
}
Nazwy Gatunek i Artyści są lepszymi nazwami kategorii, ponieważ zawierają więcej niż tylko identyfikator każdej kategorii. Refaktoryzacja, którą wcześniej opłaciliśmy. Zamiast zmieniać element ViewBag w czterech metodach, nasze zmiany zostały odizolowane od SetGenreArtistViewBag
metody .
Zmień wywołanie DropDownList w widokach tworzenia i edytowania, aby używać nowych nazw SelectList . Nowy znacznik dla widoku edycji jest pokazany poniżej:
<div class="editor-label">
@Html.LabelFor(model => model.GenreId, "Genre")
</div>
<div class="editor-field">
@Html.DropDownList("GenreId", ViewBag.Genres as SelectList)
@Html.ValidationMessageFor(model => model.GenreId)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.ArtistId, "Artist")
</div>
<div class="editor-field">
@Html.DropDownList("ArtistId", ViewBag.Artists as SelectList)
@Html.ValidationMessageFor(model => model.ArtistId)
</div>
Widok Tworzenie wymaga pustego ciągu, aby zapobiec wyświetlaniu pierwszego elementu na liście SelectList.
<div class="editor-label">
@Html.LabelFor(model => model.GenreId, "Genre" )
</div>
<div class="editor-field">
@Html.DropDownList("GenreId", ViewBag.Genres as SelectList, String.Empty)
@Html.ValidationMessageFor(model => model.GenreId)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.ArtistId, "Artist")
</div>
<div class="editor-field">
@Html.DropDownList("ArtistId", ViewBag.Artists as SelectList, String.Empty)
@Html.ValidationMessageFor(model => model.ArtistId)
</div>
Utwórz nowy album i edytuj album, aby sprawdzić, czy zmiany działają. Przetestuj kod edycji, wybierając album z gatunkiem innym niż Rock.
Używanie modelu widoku z pomocnikiem DropDownList
Utwórz nową klasę w folderze ViewModels o nazwie AlbumSelectListViewModel
. Zastąp kod w AlbumSelectListViewModel
klasie następującym kodem:
using MvcMusicStore.Models;
using System.Web.Mvc;
using System.Collections;
namespace MvcMusicStore.ViewModels {
public class AlbumSelectListViewModel {
public Album Album { get; private set; }
public SelectList Artists { get; private set; }
public SelectList Genres { get; private set; }
public AlbumSelectListViewModel(Album album,
IEnumerable artists,
IEnumerable genres) {
Album = album;
Artists = new SelectList(artists, "ArtistID", "Name", album.ArtistId);
Genres = new SelectList(genres, "GenreID", "Name", album.GenreId);
}
}
}
Konstruktor AlbumSelectListViewModel
przyjmuje album, listę artystów i gatunków i tworzy obiekt zawierający album oraz SelectList
obiekt dla gatunków i artystów.
Skompiluj projekt, AlbumSelectListViewModel
aby był dostępny podczas tworzenia widoku w następnym kroku.
Dodaj metodę EditVM
do metody StoreManagerController
. Ukończony kod jest pokazany poniżej.
//
// GET: /StoreManager/EditVM/5
public ActionResult EditVM(int id) {
Album album = db.Albums.Find(id);
if (album == null)
return HttpNotFound();
AlbumSelectListViewModel aslvm = new AlbumSelectListViewModel(album, db.Artists, db.Genres);
return View(aslvm);
}
Kliknij prawym przyciskiem myszy AlbumSelectListViewModel
, wybierz pozycję Rozwiąż, a następnie użyj mvcMusicStore.ViewModels;.
Alternatywnie możesz dodać następującą instrukcję using:
using MvcMusicStore.ViewModels;
Kliknij prawym przyciskiem myszy EditVM
i wybierz polecenie Dodaj widok. Użyj opcji przedstawionych poniżej.
Wybierz pozycję Dodaj, a następnie zastąp zawartość pliku Views\StoreManager\EditVM.cshtml następującym kodem:
@model MvcMusicStore.ViewModels.AlbumSelectListViewModel
@{
ViewBag.Title = "EditVM";
}
<h2>Edit VM</h2>
@using (Html.BeginForm("Edit","StoreManager",FormMethod.Post)) {
@Html.ValidationSummary(true)
<fieldset>
<legend>Album</legend>
@Html.HiddenFor(model => model.Album.AlbumId )
<div class="editor-label">
@Html.LabelFor(model => model.Album.GenreId, "Genre")
</div>
<div class="editor-field">
@Html.DropDownList("Album.GenreId", Model.Genres)
@Html.ValidationMessageFor(model => model.Album.GenreId)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Album.ArtistId, "Artist")
</div>
<div class="editor-field">
@Html.DropDownList("Album.ArtistId", Model.Artists)
@Html.ValidationMessageFor(model => model.Album.ArtistId)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Album.Title)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Album.Title)
@Html.ValidationMessageFor(model => model.Album.Title)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Album.Price)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Album.Price)
@Html.ValidationMessageFor(model => model.Album.Price)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Album.AlbumArtUrl)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Album.AlbumArtUrl)
@Html.ValidationMessageFor(model => model.Album.AlbumArtUrl)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
<div>
@Html.ActionLink("Back to List", "Index")
</div>
Znaczniki EditVM
są bardzo podobne do oryginalnego Edit
adiustacji z następującymi wyjątkami.
- Właściwości modelu w
Edit
widoku mają postaćmodel.property
(na przykładmodel.Title
). Właściwości modelu wEditVm
widoku mają postaćmodel.Album.property
(na przykładmodel.Album.Title
). Dzieje się tak, ponieważEditVM
widok jest przekazywany do kontenera dlaAlbum
elementu , a nieAlbum
jako wEdit
widoku. - Drugi parametr DropDownList pochodzi z modelu widoku, a nie viewBag.
- Pomocnik BeginForm w
EditVM
widoku jawnie publikuje dane z powrotem doEdit
metody akcji. Publikując z powrotem doEdit
akcji, nie musimy pisaćHTTP POST EditVM
akcji i możemy ponownie użyćHTTP POST
Edit
akcji.
Uruchom aplikację i edytuj album. Zmień adres URL, aby używał polecenia EditVM
. Zmień pole i naciśnij przycisk Zapisz, aby sprawdzić, czy kod działa.
Którego podejścia należy użyć?
Wszystkie trzy pokazane podejścia są dopuszczalne. Wielu deweloperów woli jawnie przekazać element SelectList
do DropDownList
metody przy użyciu polecenia ViewBag
. Takie podejście ma dodatkową zaletę zapewniania elastyczności używania bardziej odpowiedniej nazwy dla kolekcji. Jednym z zastrzeżeń jest to, że nie można nazwać ViewBag SelectList
obiektu o tej samej nazwie co właściwość modelu.
Niektórzy deweloperzy preferują podejście ViewModel. Inni uważają, że bardziej pełne znaczniki i wygenerowany kod HTML modelu ViewModel jest wadą.
W tej sekcji przedstawiono trzy podejścia do używania listy DropDownList z danymi kategorii. W następnej sekcji pokażemy, jak dodać nową kategorię.