Badanie sposobu ASP.NET szkieletów MVC pomocnika DropDownList
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ń element AlbumArtUrl
. Usunięcie AlbumArtUrl
sprawi, że prezentacja będzie bardziej czytelna. Ukończony kod pokazano 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. Poniżej przedstawiono kompletny kod.
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 przetestowanie 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 Administracja, 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 zostały utrwalone.
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. Poniżej przedstawiono schemat informacji o albumie:
Tabela Albums
nie przechowuje gatunku albumu i opisu, przechowuje klucz obcy do Genres
tabeli. Tabela Genres
zawiera nazwę gatunku i opis. Podobnie Albums
tabela nie zawiera nazwy artystów albumów, ale klucza obcego Artists
do 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 tabeli Artists
. Na poniższej ilustracji Albums
przedstawiono 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 jest aktualizowana przy użyciu wartości klucza obcego gatunku, a nie wyświetlanej nazwy gatunku. Na poniższej ilustracji wybrany gatunek to Disco , a artysta jest Donna Summer.
Badanie ASP.NET kodu szkieletowego 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 do obiektu ViewBag
dwa obiekty SelectList , jeden zawierający informacje o gatunku 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
znaczniki 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 przedstawionej powyżej metodzie Create
nie przekazano żadnego modelu, więc widok pobiera model o wartości nullAlbum
. W tym momencie tworzymy nowy album, abyśmy nie mieli dla niego żadnych Album
danych.
Przeciążenie Html.DropDownList pokazane powyżej przyjmuje nazwę pola, które ma być powiązane z modelem. Ta nazwa jest również używana do wyszukiwania obiektu ViewBag zawierającego obiekt SelectList . Przy użyciu tego przeciążenia należy nazwać obiekt GenreId
ViewBag SelectList . Drugi parametr (String.Empty
) to tekst, który ma być wyświetlany, gdy nie wybrano żadnego elementu. Jest to dokładnie to, czego chcemy podczas tworzenia nowego albumu. Jeśli usunięto drugi parametr i użyto następującego kodu:
@Html.DropDownList("GenreId")
Lista wyboru domyślnie zostanie ustawiona na pierwszy element lub Skała 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ązań 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
Obie metody 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 kontrolki 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 pokazano 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 Listy rozwijanej :
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ść dla SelectList
właściwości ma nazwę GenreId
, pomocnik DropDownList użyje GenreId
listy SelectList w obiekcie ViewBag. W poniższym przeciążeniu SelectList
DropDownList element 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 powyższego przeciążenia. Zrób to dla kategorii Gatunek. Ukończony kod przedstawiono poniżej:
@Html.DropDownList("GenreId", ViewBag.GenreId as SelectList)
Uruchom aplikację i kliknij link Administracja, 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 podano żadnej wybranej wartości, przeglądarki są domyślnie wybierane jako pierwszy element 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 przedstawiono 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 Gatunki i Artyści są lepszymi nazwami kategorii, ponieważ zawierają więcej niż tylko identyfikator każdej kategorii. Refaktoryzacja, którą zrobiliśmy wcześniej, opłaciła się. Zamiast zmieniać element ViewBag w czterech metodach, nasze zmiany zostały odizolowane od SetGenreArtistViewBag
metody .
Zmień wywołanie Listy rozwijanej w widokach tworzenia i edytowania, aby używać nowych nazw SelectList . Poniżej przedstawiono nowe znaczniki dla widoku edycji:
<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 Listy rozwijanej
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 dla gatunków i SelectList
artystów.
Skompiluj projekt tak, aby AlbumSelectListViewModel
był dostępny po utworzeniu widoku w następnym kroku.
Dodaj metodę EditVM
do elementu 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
pozycję , wybierz pozycję Rozwiąż, a następnie użyj polecenia MvcMusicStore.ViewModels;.
Alternatywnie można dodać następującą instrukcję using:
using MvcMusicStore.ViewModels;
Kliknij prawym przyciskiem myszy EditVM
i wybierz pozycję Dodaj widok. Użyj opcji przedstawionych poniżej.
Wybierz pozycję Dodaj, a następnie zastąp zawartość pliku Views\StoreManager\EditVM.cshtml następującymi elementami:
@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 są formąmodel.property
(na przykładmodel.Title
). Właściwości modelu wEditVm
widoku są formąmodel.Album.property
(na przykładmodel.Album.Title
). Dzieje się tak, ponieważEditVM
widok jest przekazywany do kontenera dla elementuAlbum
, a nieAlbum
jako wEdit
widoku. - Drugi parametr DropDownList pochodzi z modelu widoku, a nie ViewBag.
- Pomocnik BeginForm w
EditVM
widoku jawnie publikuje z powrotem doEdit
metody akcji. Publikując ponownieEdit
akcję, nie musimy pisaćHTTP POST EditVM
akcji i ponownie użyćHTTP POST
Edit
akcji.
Uruchom aplikację i edytuj album. Zmień adres URL, aby użyć 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
elementu za pomocą polecenia ViewBag
. To podejście ma dodatkową zaletę, zapewniając elastyczność 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 adiustowanie i wygenerowany kod HTML modelu ViewModel jest wadą.
W tej sekcji przedstawiono trzy podejścia do używania listy Rozwijanej z danymi kategorii. W następnej sekcji pokażemy, jak dodać nową kategorię.
Opinia
https://aka.ms/ContentUserFeedback.
Dostępne już wkrótce: W 2024 r. będziemy stopniowo wycofywać zgłoszenia z serwisu GitHub jako mechanizm przesyłania opinii na temat zawartości i zastępować go nowym systemem opinii. Aby uzyskać więcej informacji, sprawdź:Prześlij i wyświetl opinię dla