Implementowanie interfejsu użytkownika typu lista/szczegóły przy użyciu kontrolerów i widoków
autor: Microsoft
Jest to krok 4 bezpłatnego samouczka aplikacji "NerdDinner" , który zawiera instrukcje dotyczące tworzenia małej, ale kompletnej aplikacji internetowej przy użyciu ASP.NET MVC 1.
Krok 4 pokazuje, jak dodać kontroler do aplikacji, która korzysta z naszego modelu, aby zapewnić użytkownikom środowisko nawigacji listy danych/szczegółów na kolacje w naszej witrynie NerdDinner.
Jeśli używasz ASP.NET MVC 3, zalecamy skorzystanie z samouczków Wprowadzenie With MVC 3 lub MVC Music Store.
NerdDinner — krok 4: kontrolery i widoki
W przypadku tradycyjnych struktur internetowych (klasycznych platform ASP, PHP, ASP.NET Web Forms itp.) przychodzące adresy URL są zwykle mapowane na pliki na dysku. Na przykład: żądanie adresu URL, takiego jak "/Products.aspx" lub "/Products.php", może zostać przetworzone przez plik "Products.aspx" lub "Products.php".
Internetowe struktury MVC mapować adresy URL na kod serwera w nieco inny sposób. Zamiast mapować przychodzące adresy URL na pliki, zamiast mapować adresy URL na metody w klasach. Te klasy są nazywane "kontrolerami" i są odpowiedzialne za przetwarzanie przychodzących żądań HTTP, obsługę danych wejściowych użytkownika, pobieranie i zapisywanie danych oraz określanie odpowiedzi na potrzeby wysyłania z powrotem do klienta (wyświetlanie kodu HTML, pobieranie pliku, przekierowywanie do innego adresu URL itp.).
Teraz, gdy utworzyliśmy podstawowy model dla naszej aplikacji NerdDinner, następnym krokiem będzie dodanie kontrolera do aplikacji, która z niej korzysta, aby zapewnić użytkownikom środowisko nawigacji po listach danych/szczegółach dla kolacji w naszej witrynie.
Dodawanie kontrolera DinnersController
Zaczniemy od kliknięcia prawym przyciskiem myszy folderu "Controllers" w naszym projekcie internetowym, a następnie wybrania polecenia menu Dodaj kontroler> (można również wykonać to polecenie, wpisując Ctrl-M, Ctrl-C):
Spowoduje to wyświetlenie okna dialogowego "Dodawanie kontrolera":
Nadamy nowej kontrolerowi nazwę "DinnersController" i klikniemy przycisk "Dodaj". Program Visual Studio doda plik DinnersController.cs w katalogu \Controllers:
Otworzy również nową klasę DinnersController w edytorze kodu.
Dodawanie metod akcji Index() i Details() do klasy DinnersController
Chcemy umożliwić odwiedzającym korzystanie z naszej aplikacji w celu przeglądania listy nadchodzących kolacji i umożliwienia im kliknięcia dowolnej kolacji na liście, aby wyświetlić szczegółowe informacje o niej. W tym celu opublikujemy następujące adresy URL z naszej aplikacji:
Adres URL | Cel |
---|---|
/Kolacje/ | Wyświetlanie listy HTML nadchodzących kolacji |
/Dinners/Details/[id] | Wyświetl szczegółowe informacje o określonej kolacji wskazanej przez parametr "id" osadzony w adresie URL — który będzie zgodny z identyfikatorem DinnerID kolacji w bazie danych. Na przykład: /Dinners/Details/2 wyświetli stronę HTML ze szczegółami dotyczącymi kolacji, której wartość DinnerID wynosi 2. |
Opublikujemy początkowe implementacje tych adresów URL, dodając dwie publiczne "metody akcji" do klasy DinnersController, jak poniżej:
public class DinnersController : Controller {
//
// HTTP-GET: /Dinners/
public void Index() {
Response.Write("<h1>Coming Soon: Dinners</h1>");
}
//
// HTTP-GET: /Dinners/Details/2
public void Details(int id) {
Response.Write("<h1>Details DinnerID: " + id + "</h1>");
}
}
Następnie uruchomimy aplikację NerdDinner i użyjemy przeglądarki, aby je wywołać. Wpisanie w adresie URL "/Dinners/" spowoduje uruchomienie metody Index() i wyśle z powrotem następującą odpowiedź:
Wpisanie adresu URL "/Dinners/Details/2" spowoduje uruchomienie metody Details() i wysłanie z powrotem następującej odpowiedzi:
Być może zastanawiasz się — jak ASP.NET MVC wiedział, aby utworzyć naszą klasę DinnersController i wywołać te metody? Aby zrozumieć, że przyjrzyjmy się, jak działa routing.
Opis routingu MVC ASP.NET
ASP.NET MVC zawiera zaawansowany aparat routingu adresów URL, który zapewnia dużą elastyczność w kontrolowaniu sposobu mapowania adresów URL na klasy kontrolerów. Pozwala nam to całkowicie dostosować sposób, w jaki ASP.NET MVC wybiera klasę kontrolera do utworzenia, którą metodę wywołać na niej, a także skonfigurować różne sposoby automatycznego analizowania zmiennych z adresu URL/ciągu zapytania i przekazywania do metody jako argumentów parametrów. Zapewnia elastyczność, aby całkowicie zoptymalizować witrynę pod kątem optymalizacji aparatu wyszukiwania (optymalizacja aparatu wyszukiwania), a także opublikować dowolną strukturę adresów URL z poziomu aplikacji.
Domyślnie nowe projekty ASP.NET MVC są dostarczane ze wstępnie skonfigurowanym zestawem reguł routingu adresów URL, które zostały już zarejestrowane. Dzięki temu możemy łatwo rozpocząć pracę z aplikacją bez konieczności jawnego konfigurowania niczego. Domyślne rejestracje reguł routingu można znaleźć w klasie "Application" naszych projektów — którą można otworzyć, klikając dwukrotnie plik "Global.asax" w katalogu głównym naszego projektu:
Domyślne reguły routingu MVC ASP.NET są rejestrowane w metodzie "RegisterRoutes" tej klasy:
public void RegisterRoutes(RouteCollection routes) {
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL w/ params
new { controller="Home", action="Index",id="" } // Param defaults
);
}
"trasy. Wywołanie metody MapRoute()" powyżej rejestruje domyślną regułę routingu, która mapuje przychodzące adresy URL na klasy kontrolera przy użyciu formatu ADRESU URL: "/{controller}/{action}/{id}" — gdzie "kontroler" jest nazwą klasy kontrolera, aby utworzyć wystąpienie klasy kontrolera, "action" jest nazwą metody publicznej do wywołania na niej, a "id" jest opcjonalnym parametrem osadzonym w adresie URL, który można przekazać jako argument do metody. Trzeci parametr przekazany do wywołania metody "MapRoute()" jest zestawem wartości domyślnych do użycia dla wartości kontrolera/akcji/identyfikatora w przypadku, gdy nie są obecne w adresie URL (Controller = "Home", Action="Index", Id="").
Poniżej znajduje się tabela, która pokazuje, jak różne adresy URL są mapowane przy użyciu domyślnej reguły trasy "/{controllers}/{action}/{id}"route:
Adres URL | Klasa kontrolera | Action, metoda | Przekazane parametry |
---|---|---|---|
/Dinners/Details/2 | DinnersController | Details(id) | id=2 |
/Dinners/Edit/5 | DinnersController | Edit(id) | id=5 |
/Dinners/Create | DinnersController | Create() | Nie dotyczy |
/Kolacje | DinnersController | Index() | Nie dotyczy |
/W: strona główna | HomeController | Index() | Nie dotyczy |
/ | HomeController | Index() | Nie dotyczy |
W ostatnich trzech wierszach są wyświetlane wartości domyślne (Controller = Home, Action = Index, Id = "") używane. Ponieważ metoda "Index" jest zarejestrowana jako domyślna nazwa akcji, jeśli jej nie określono, adresy URL "/Dinners" i "/Home" powodują wywołanie metody akcji Index() w klasach kontrolera. Ponieważ kontroler "Home" jest zarejestrowany jako kontroler domyślny, jeśli nie został określony, adres URL "/" powoduje utworzenie HomeController i metodę akcji Index() na nim do wywołania.
Jeśli nie podobają Ci się te domyślne reguły routingu adresów URL, dobrą wiadomością jest to, że można je łatwo zmienić — po prostu edytuj je w metodzie RegisterRoutes powyżej. Jednak w przypadku naszej aplikacji NerdDinner nie zmienimy żadnych domyślnych reguł routingu adresów URL — zamiast tego użyjemy ich tak, jak jest.
Korzystanie z repozytorium DinnerRepository z elementu DinnersController
Teraz zastąpmy naszą bieżącą implementację metod akcji Index() i Details() dinnersController za pomocą implementacji, które używają naszego modelu.
Użyjemy utworzonej wcześniej klasy DinnerRepository, aby zaimplementować zachowanie. Zaczniemy od dodania instrukcji "using", która odwołuje się do przestrzeni nazw "NerdDinner.Models", a następnie zadeklarujemy wystąpienie naszego repozytorium DinnerRepository jako pole w klasie DinnerController.
W dalszej części tego rozdziału przedstawimy koncepcję "Wstrzykiwanie zależności" i pokażemy innym sposobem, aby kontrolery mogli uzyskać odwołanie do repozytorium DinnerRepository, które umożliwia lepsze testowanie jednostkowe — ale na razie utworzymy tylko wystąpienie naszego repozytorium kolacji w tekście, jak pokazano poniżej.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using NerdDinner.Models;
namespace NerdDinner.Controllers {
public class DinnersController : Controller {
DinnerRepository dinnerRepository = new DinnerRepository();
//
// GET: /Dinners/
public void Index() {
var dinners = dinnerRepository.FindUpcomingDinners().ToList();
}
//
// GET: /Dinners/Details/2
public void Details(int id) {
Dinner dinner = dinnerRepository.GetDinner(id);
}
}
}
Teraz możemy wygenerować odpowiedź HTML z powrotem przy użyciu pobranych obiektów modelu danych.
Używanie widoków z naszym kontrolerem
Chociaż istnieje możliwość napisania kodu w naszych metodach akcji w celu zebrania kodu HTML, a następnie użycia metody pomocnika Response.Write() do wysłania go z powrotem do klienta, takie podejście staje się dość nieporęcznie szybkie. Znacznie lepszym rozwiązaniem jest wykonanie tylko aplikacji i logiki danych wewnątrz metod akcji DinnersController, a następnie przekazanie danych potrzebnych do renderowania odpowiedzi HTML na oddzielny szablon "view", który jest odpowiedzialny za wyprowadzenie reprezentacji HTML. Jak zobaczymy za chwilę, szablon "view" jest plikiem tekstowym, który zazwyczaj zawiera kombinację znaczników HTML i osadzonego kodu renderowania.
Oddzielenie logiki kontrolera od renderowania widoku przynosi kilka dużych korzyści. W szczególności pomaga wymusić wyraźne "rozdzielenie problemów" między kodem aplikacji i kodem formatowania/renderowania interfejsu użytkownika. Ułatwia to testowanie logiki aplikacji jednostkowej w izolacji od logiki renderowania interfejsu użytkownika. Ułatwia to późniejsze modyfikowanie szablonów renderowania interfejsu użytkownika bez konieczności wprowadzania zmian w kodzie aplikacji. Dzięki niej deweloperzy i projektanci mogą łatwiej współpracować nad projektami.
Możemy zaktualizować klasę DinnersController, aby wskazać, że chcemy użyć szablonu widoku do wysłania odpowiedzi interfejsu użytkownika HTML, zmieniając podpisy metody naszych dwóch metod akcji z zwracania typu "void", aby zamiast tego miał zwracany typ "ActionResult". Następnie możemy wywołać metodę pomocnika View() w klasie bazowej Kontroler, aby zwrócić obiekt "ViewResult", jak pokazano poniżej:
public class DinnersController : Controller {
DinnerRepository dinnerRepository = new DinnerRepository();
//
// GET: /Dinners/
public ActionResult Index() {
var dinners = dinnerRepository.FindUpcomingDinners().ToList();
return View("Index", dinners);
}
//
// GET: /Dinners/Details/2
public ActionResult Details(int id) {
Dinner dinner = dinnerRepository.GetDinner(id);
if (dinner == null)
return View("NotFound");
else
return View("Details", dinner);
}
}
Podpis metody pomocnika View(), która jest używana powyżej, wygląda następująco:
Pierwszy parametr metody pomocnika View() to nazwa pliku szablonu widoku, którego chcemy użyć do renderowania odpowiedzi HTML. Drugi parametr jest obiektem modelu zawierającym dane wymagane przez szablon widoku w celu renderowania odpowiedzi HTML.
W naszej metodzie akcji Index() wywołujemy metodę pomocnika View() i wskazujemy, że chcemy renderować listę HTML kolacji przy użyciu szablonu widoku "Indeks". Przekazujemy szablon widoku sekwencję obiektów Dinner w celu wygenerowania listy z:
//
// GET: /Dinners/
public ActionResult Index() {
var dinners = dinnerRepository.FindUpcomingDinners().ToList();
return View("Index", dinners);
}
W naszej metodzie akcji Details() próbujemy pobrać obiekt Dinner przy użyciu identyfikatora podanego w adresie URL. Jeśli zostanie znaleziona prawidłowa kolacja, wywołamy metodę pomocnika View(), wskazując, że chcemy użyć szablonu widoku "Szczegóły" do renderowania pobranego obiektu Obiad. Jeśli zażądano nieprawidłowej kolacji, zostanie wyświetlony pomocny komunikat o błędzie wskazujący, że kolacja nie istnieje przy użyciu szablonu widoku "NotFound" (i przeciążonej wersji metody pomocnika View(), która po prostu przyjmuje nazwę szablonu):
//
// GET: /Dinners/Details/2
public ActionResult Details(int id) {
Dinner dinner = dinnerRepository.FindDinner(id);
if (dinner == null)
return View("NotFound");
else
return View("Details", dinner);
}
Teraz zaimplementujmy szablony widoków "NotFound", "Details" i "Index".
Implementowanie szablonu widoku "NotFound"
Zaczniemy od zaimplementowania szablonu widoku "NotFound" — który wyświetla przyjazny komunikat o błędzie wskazujący, że nie można odnaleźć żądanej kolacji.
Utworzymy nowy szablon widoku, ustawiając kursor tekstu w metodzie akcji kontrolera, a następnie kliknij prawym przyciskiem myszy i wybierz polecenie menu "Dodaj widok" (możemy również wykonać to polecenie, wpisując Ctrl-M, Ctrl-V):
Spowoduje to wyświetlenie okna dialogowego "Dodaj widok", jak pokazano poniżej. Domyślnie okno dialogowe wstępnie wypełni nazwę widoku do utworzenia w celu dopasowania nazwy metody akcji kursor był w momencie uruchomienia okna dialogowego (w tym przypadku "Szczegóły"). Ponieważ chcemy najpierw zaimplementować szablon "NotFound", zastąpimy tę nazwę widoku i ustawimy ją na wartość "NotFound":
Po kliknięciu przycisku "Dodaj" program Visual Studio utworzy nowy szablon widoku "NotFound.aspx" dla nas w katalogu "\Views\Dinners" (który również zostanie utworzony, jeśli katalog jeszcze nie istnieje):
Zostanie również otwarty nasz nowy szablon widoku "NotFound.aspx" w edytorze kodu:
Szablony wyświetlania domyślnie mają dwa "regiony zawartości", w których można dodawać zawartość i kod. Pierwszy umożliwia dostosowanie "tytułu" strony HTML wysłanej z powrotem. Drugi umożliwia dostosowanie "głównej zawartości" strony HTML wysłanej z powrotem.
Aby zaimplementować szablon widoku "NotFound", dodamy podstawową zawartość:
<asp:Content ID="Title" ContentPlaceHolderID="TitleContent" runat="server">
Dinner Not Found
</asp:Content>
<asp:Content ID="Main" ContentPlaceHolderID="MainContent" runat="server">
<h2>Dinner Not Found</h2>
<p>Sorry - but the dinner you requested doesn't exist or was deleted.</p>
</asp:Content>
Następnie możemy wypróbować go w przeglądarce. W tym celu zażądajmy adresu URL "/Dinners/Details/9999". Spowoduje to odwołanie do kolacji, która nie istnieje obecnie w bazie danych, i spowoduje, że nasza metoda akcji DinnersController.Details() renderuje nasz szablon widoku "NotFound":
Jedną z rzeczy, które zauważysz na powyższym zrzucie ekranu, jest to, że nasz podstawowy szablon widoku odziedziczył kilka kodu HTML otaczającego główną zawartość na ekranie. Dzieje się tak, ponieważ nasz szablon widoku używa szablonu "strona wzorcowa", który umożliwia stosowanie spójnego układu we wszystkich widokach w witrynie. Omówimy sposób działania stron wzorcowych w dalszej części tego samouczka.
Implementowanie szablonu widoku "Szczegóły"
Teraz zaimplementujmy szablon widoku "Szczegóły", który wygeneruje kod HTML dla pojedynczego modelu kolacji.
W tym celu umieścimy kursor tekstu w metodzie akcji Szczegóły, a następnie kliknij prawym przyciskiem myszy i wybierz polecenie menu "Dodaj widok" (lub naciśnij klawisze Ctrl-M, Ctrl-V):
Spowoduje to wyświetlenie okna dialogowego "Dodawanie widoku". Zachowamy domyślną nazwę widoku ("Szczegóły"). W oknie dialogowym wybierzemy również pole wyboru "Utwórz silnie typizowany widok" i wybierzemy (przy użyciu listy rozwijanej pola kombi) nazwę typu modelu, który przekazujemy z kontrolera do widoku. W tym widoku przekazujemy obiekt Dinner (w pełni kwalifikowana nazwa tego typu to: "NerdDinner.Models.Dinner"):
W przeciwieństwie do poprzedniego szablonu, w którym postanowiliśmy utworzyć widok "Pusty widok", tym razem wybierzemy automatyczne "szkielet" widoku przy użyciu szablonu "Szczegóły". Możemy to wskazać, zmieniając listę rozwijaną "Wyświetl zawartość" w oknie dialogowym powyżej.
"Tworzenie szkieletu" spowoduje wygenerowanie początkowej implementacji szablonu widoku szczegółów na podstawie obiektu Dinner, który przekazujemy do niego. Dzięki temu możemy szybko rozpocząć pracę nad implementacją szablonu widoku.
Po kliknięciu przycisku "Dodaj" program Visual Studio utworzy nowy plik szablonu "Details.aspx" dla nas w katalogu "\Views\Dinners":
Spowoduje to również otwarcie nowego szablonu widoku "Details.aspx" w edytorze kodu. Będzie zawierać początkową implementację szkieletu widoku szczegółów na podstawie modelu kolacji. Aparat tworzenia szkieletów używa odbicia platformy .NET, aby przyjrzeć się właściwościom publicznym uwidocznionym w przekazanej klasie i doda odpowiednią zawartość na podstawie każdego znalezionego typu:
<asp:Content ID="Title" ContentPlaceHolderID="TitleContent" runat="server">
Details
</asp:Content>
<asp:Content ID="Main" ContentPlaceHolderID="MainContent" runat="server">
<h2>Details</h2>
<fieldset>
<legend>Fields</legend>
<p>
DinnerID:
<%=Html.Encode(Model.DinnerID) %>
</p>
<p>
Title:
<%=Html.Encode(Model.Title) %>
</p>
<p>
EventDate:
<%= Html.Encode(String.Format("{0:g}", Model.EventDate)) %>
</p>
<p>
Description:
<%=Html.Encode(Model.Description) %>
</p>
<p>
HostedBy:
<%=Html.Encode(Model.HostedBy) %>
</p>
<p>
ContactPhone:
<%=Html.Encode(Model.ContactPhone) %>
</p>
<p>
Address:
<%=Html.Encode(Model.Address) %>
</p>
<p>
Country:
<%=Html.Encode(Model.Country) %>
</p>
<p>
Latitude:
<%= Html.Encode(String.Format("{0:F}",Model.Latitude)) %>
</p>
<p>
Longitude:
<%= Html.Encode(String.Format("{0:F}",Model.Longitude)) %>
</p>
</fieldset>
<p>
<%=Html.ActionLink("Edit","Edit", new { id=Model.DinnerID }) %>|
<%=Html.ActionLink("Back to List", "Index") %>
</p>
</asp:Content>
Możemy zażądać adresu URL "/Dinners/Details/1" , aby zobaczyć, jak wygląda implementacja szkieletu "details" w przeglądarce. Użycie tego adresu URL spowoduje wyświetlenie jednej z kolacji, które ręcznie dodaliśmy do bazy danych podczas tworzenia:
Spowoduje to szybkie uruchomienie i zapewni nam wstępną implementację widoku Details.aspx. Następnie możemy przejść i dostosować go, aby dostosować interfejs użytkownika do naszej satysfakcji.
Gdy przyjrzymy się bliżej szablonowi Details.aspx, przekonamy się, że zawiera on statyczny kod HTML, a także osadzony kod renderowania. <% %> nuggets kodu wykonuje kod podczas renderowania szablonu widoku, a <%= %> nuggets kodu wykonują zawarty w nich kod, a następnie renderuj wynik do strumienia wyjściowego szablonu.
Możemy napisać kod w naszym widoku, który uzyskuje dostęp do obiektu modelu "Kolacja", który został przekazany z kontrolera przy użyciu silnie typizowanej właściwości "Model". Program Visual Studio zapewnia pełną funkcję IntelliSense kodu podczas uzyskiwania dostępu do tej właściwości "Model" w edytorze:
Wprowadźmy pewne poprawki, aby źródło naszego końcowego szablonu widoku Szczegóły wyglądało następująco:
<asp:Content ID="Title" ContentPlaceHolderID="TitleContent" runat="server">
Dinner: <%=Html.Encode(Model.Title) %>
</asp:Content>
<asp:Content ID="Main" ContentPlaceHolderID="MainContent" runat="server">
<h2><%=Html.Encode(Model.Title) %></h2>
<p>
<strong>When:</strong>
<%=Model.EventDate.ToShortDateString() %>
<strong>@</strong>
<%=Model.EventDate.ToShortTimeString() %>
</p>
<p>
<strong>Where:</strong>
<%=Html.Encode(Model.Address) %>,
<%=Html.Encode(Model.Country) %>
</p>
<p>
<strong>Description:</strong>
<%=Html.Encode(Model.Description) %>
</p>
<p>
<strong>Organizer:</strong>
<%=Html.Encode(Model.HostedBy) %>
(<%=Html.Encode(Model.ContactPhone) %>)
</p>
<%= Html.ActionLink("Edit Dinner", "Edit", new { id=Model.DinnerID })%> |
<%= Html.ActionLink("Delete Dinner","Delete", new { id=Model.DinnerID})%>
</asp:Content>
Gdy ponownie uzyskamy dostęp do adresu URL "/Dinners/Details/1" , będzie on teraz renderowany w następujący sposób:
Implementowanie szablonu widoku "Indeks"
Teraz zaimplementujmy szablon widoku "Indeks", który wygeneruje listę nadchodzących kolacji. Aby to zrobić, umieść kursor tekstu w metodzie akcji Indeks, a następnie kliknij prawym przyciskiem myszy i wybierz polecenie menu "Dodaj widok" (lub naciśnij klawisze Ctrl-M, Ctrl-V).
W oknie dialogowym "Dodaj widok" zachowamy szablon widoku o nazwie "Indeks" i zaznaczymy pole wyboru "Utwórz widok silnie typizowane". Tym razem wybierzemy opcję automatycznego wygenerowania szablonu widoku "Lista", a następnie wybierzemy pozycję "NerdDinner.Models.Dinner" jako typ modelu przekazany do widoku (co oznacza, że wskazujemy, że tworzymy szkielet "Lista" spowoduje przekazanie sekwencji obiektów kolacji z kontrolera do widoku):
Po kliknięciu przycisku "Dodaj" program Visual Studio utworzy nowy plik szablonu "Index.aspx" dla nas w katalogu "\Views\Dinners". Będzie ona "szkieletem" początkowej implementacji, która udostępnia listę tabel HTML przekazanych do widoku.
Gdy uruchomimy aplikację i uzyskamy dostęp do adresu URL "/Dinners/" , spowoduje to renderowanie listy kolacji w następujący sposób:
Powyższe rozwiązanie tabeli daje nam układ danych kolacji przypominający siatkę — co nie jest do końca potrzebne dla naszej oferty obiadowej dla konsumentów. Możemy zaktualizować szablon widoku Index.aspx i zmodyfikować go w celu wyświetlenia mniejszej liczby kolumn danych, a następnie użyć <elementu ul> do renderowania ich zamiast tabeli przy użyciu poniższego kodu:
<asp:Content ID="Main" ContentPlaceHolderID="MainContent" runat="server">
<h2>Upcoming Dinners</h2>
<ul>
<% foreach (var dinner in Model) { %>
<li>
<%=Html.Encode(dinner.Title) %>
on
<%=Html.Encode(dinner.EventDate.ToShortDateString())%>
@
<%=Html.Encode(dinner.EventDate.ToShortTimeString())%>
</li>
<% } %>
</ul>
</asp:Content>
Używamy słowa kluczowego "var" w powyższej instrukcji foreach, ponieważ przeprowadzamy pętlę na każdej kolacji w modelu. Osoby nieznane w języku C# 3.0 mogą myśleć, że użycie "var" oznacza, że obiekt kolacji jest opóźniony. Oznacza to, że kompilator używa wnioskowania typu względem silnie typizowanej właściwości "Model" (czyli typu "IEnumerable<Dinner>") i kompilowania lokalnej zmiennej "dinner" jako typu kolacji — co oznacza, że uzyskujemy pełną funkcję IntelliSense i sprawdzanie czasu kompilacji dla niej w blokach kodu:
Po naciśnięciu przycisku odświeżenia w adresie URL /Dinners w przeglądarce nasz zaktualizowany widok wygląda teraz następująco:
Wygląda to lepiej - ale jeszcze nie jest. Ostatnim krokiem jest umożliwienie użytkownikom końcowym klikania poszczególnych kolacji na liście i wyświetlania szczegółowych informacji o nich. Zaimplementujemy to przez renderowanie elementów hiperlinku HTML, które łączą się z metodą akcji Szczegóły w elemecie DinnersController.
Te hiperlinki możemy wygenerować w naszym widoku Indeks na jeden z dwóch sposobów. Pierwszym z nich jest ręczne utworzenie kodu HTML <elementów>, takich jak poniżej, gdzie osadzamy <bloki %> w elemecie <> HTML:
Alternatywną metodą, którą możemy użyć, jest wykorzystanie wbudowanej metody pomocnika "Html.ActionLink()" w ASP.NET MVC, która obsługuje programowe tworzenie kodu HTML <> elementu, który łączy się z inną metodą akcji na kontrolerze:
<%= Html.ActionLink(dinner.Title, "Details", new { id=dinner.DinnerID }) %>
Pierwszy parametr metody pomocnika Html.ActionLink() jest tekstem linku do wyświetlenia (w tym przypadku tytułem kolacji), drugim parametrem jest nazwa akcji Kontroler, do którego chcemy wygenerować łącze (w tym przypadku metoda Details), a trzeci parametr jest zestawem parametrów do wysłania do akcji (zaimplementowany jako anonimowy typ z nazwą/wartościami właściwości). W tym przypadku określamy parametr "id" kolacji, z którym chcemy się połączyć, a ponieważ domyślna reguła routingu adresów URL w ASP.NET MVC to "{Controller}/{Action}/{id}" metoda pomocnika Html.ActionLink() wygeneruje następujące dane wyjściowe:
<a href="/Dinners/Details/1">.NET Futures</a>
W naszym widoku Index.aspx użyjemy metody pomocnika Html.ActionLink() i każda kolacja na liście zawiera link do odpowiedniego adresu URL szczegółów:
<asp:Content ID="Title" ContentPlaceHolderID="TitleContent" runat="server">
Upcoming Dinners
</asp:Content>
<asp:Content ID="Main" ContentPlaceHolderID="MainContent" runat="server">
<h2>Upcoming Dinners</h2>
<ul>
<% foreach (var dinner in Model) { %>
<li>
<%=Html.ActionLink(dinner.Title, "Details", new { id=dinner.DinnerID }) %>
on
<%=Html.Encode(dinner.EventDate.ToShortDateString())%>
@
<%=Html.Encode(dinner.EventDate.ToShortTimeString())%>
</li>
<% } %>
</ul>
</asp:Content>
A teraz, gdy trafimy na adres URL /Dinners , nasza lista kolacji wygląda następująco:
Po kliknięciu dowolnej z kolacji na liście przejdziemy w celu wyświetlenia szczegółowych informacji o nim:
Nazewnictwo oparte na konwencji i struktura katalogu \Views
ASP.NET aplikacje MVC domyślnie używają struktury nazewnictwa katalogów opartych na konwencji podczas rozpoznawania szablonów widoku. Dzięki temu deweloperzy nie muszą w pełni kwalifikować ścieżki lokalizacji podczas odwoływania się do widoków z klasy Kontroler. Domyślnie ASP.NET MVC będzie szukać pliku szablonu widoku w katalogu *\Views[ControllerName]* pod aplikacją.
Na przykład pracujemy nad klasą DinnersController, która jawnie odwołuje się do trzech szablonów widoków: "Index", "Details" i "NotFound". ASP.NET MVC domyślnie wyszukuje te widoki w katalogu \Views\Dinners poniżej katalogu głównego aplikacji:
Zwróć uwagę na to, jak obecnie istnieją trzy klasy kontrolerów w projekcie (DinnersController, HomeController i AccountController — ostatnie dwa zostały dodane domyślnie podczas tworzenia projektu) i istnieją trzy podkatalogy (jeden dla każdego kontrolera) w katalogu \Views.
Widoki, do których odwołuje się kontrolery Home and Accounts, automatycznie rozpoznają szablony widoków z odpowiednich katalogów \Views\Home i \Views\Account . Katalog \Views\Shared umożliwia przechowywanie szablonów widoków, które są ponownie używane na wielu kontrolerach w aplikacji. Gdy ASP.NET MVC podejmie próbę rozwiązania szablonu widoku, najpierw sprawdzi się w katalogu specyficznym dla \Views[Controller] i jeśli nie będzie można znaleźć szablonu widoku, będzie on wyglądał w katalogu \Views\Shared .
Jeśli chodzi o nazewnictwo poszczególnych szablonów widoków, zalecane wskazówki to, aby szablon widoku miał taką samą nazwę jak metoda akcji, która spowodowała renderowanie. Na przykład powyżej metody akcji "Indeks" jest używany widok "Index" w celu renderowania wyniku widoku, a metoda akcji "Details" używa widoku "Details" w celu renderowania wyników. Dzięki temu można szybko sprawdzić, który szablon jest skojarzony z każdą akcją.
Deweloperzy nie muszą jawnie określać nazwy szablonu widoku, gdy szablon widoku ma taką samą nazwę jak metoda akcji wywoływana na kontrolerze. Zamiast tego możemy przekazać obiekt modelu do metody pomocnika "View()" (bez określania nazwy widoku), a ASP.NET MVC automatycznie wywnioskuje, że chcemy użyć szablonu widoku \Views[ControllerName][ActionName] na dysku, aby go renderować.
Dzięki temu możemy trochę wyczyścić kod kontrolera i uniknąć dwukrotnego duplikowania nazwy w naszym kodzie:
public class DinnersController : Controller {
DinnerRepository dinnerRepository = new DinnerRepository();
//
// GET: /Dinners/
public ActionResult Index() {
var dinners = dinnerRepository.FindUpcomingDinners().ToList();
return View(dinners);
}
//
// GET: /Dinners/Details/2
public ActionResult Details(int id) {
Dinner dinner = dinnerRepository.GetDinner(id);
if (dinner == null)
return View("NotFound");
else
return View(dinner);
}
}
Powyższy kod jest potrzebny do zaimplementowania ładnego środowiska listy/szczegółów kolacji dla witryny.
Następny krok
Mamy teraz ładne środowisko przeglądania kolacji zbudowane.
Włączmy teraz obsługę edycji formularzy danych CRUD (Create, Read, Update, Delete).