4 AS SP.NET MVC Widok 1
1. Cel zajęć Celem zajęć jest zapoznanie się z metodami tworzenia widoków w ASP.NET MVC 2. Wprowadzenie Na poprzednich zajęciach stworzyliśmy prostą aplikację pokazującą podstawowe elementy Framework ASP.NET MVC. Dane pomiędzy kontrolerem, a widokiem przekazywane były za pomocą słownika ViewData. Metoda ta, choć prosta ma szereg wad. Po pierwsze, wymaga generowania dużej liczby linii kodu. Po drugie kluczem w słowniku jest łańcuch znaków, więc na etapie kompilacji nie ma możliwości, aby sprawdzić, czy nie pomyliliśmy się przy wprowadzaniu klucza w widoku lub w kontrolerze. Po trzecie słownik ten jako dane przechowuje obiekty typu object, co wymaga dokonania odpowiedniego rzutowania, poprawności którego również nie jesteśmy wstanie sprawdzić na etapie kompilacji. Po czwarte metoda ta nie pozwala na wykorzystanie możliwości Frameworka ASP.NET MVC w zakresie automatycznego generowania widoków. Na dzisiejszych zajęciach poznamy takie elementy platformy ASP.NET MVC jak: Widoki o ścisłej kontroli typów (Strongly-Type Views) Widoki częściowe o ścisłej kontroli typów (Strongly-Type Partial Views) Szablony Helpery 3. Zadanie Proszę przebudować aplikację, która była utworzona na poprzednich zajęciach, tak aby wykorzystywała elementy widoków wspomnianych w poprzednim punkcie. W tym celu należy wykonać odpowiednie kroki: I. Przygotowanie projektu a) Otwieramy projekt przygotowany na poprzednich zajęciach. b) Otwieramy okno Solution Explorer i usuwamy z niego wszystkie elementy jakie w znajdują się w katalogach Views/Osoba oraz Views/ /Telefon. W tym celu należy zaznaczyć wszystkie pliki znajdujące się w tych katalogach i nacisnąć klawisz Del. 2
II. Przygotowanie kontrolera a) Otwieramy kontroler OsobaController i ze wszystkich metod usuwamy odwołania do słownika ViewData, w rezultacie powinniśmy otrzymać kod (pokazane są tylko te metody, które uległy modyfikacji): public ActionResult Index() return View(); public ActionResult Details(int id) Osob bamodel osoba = _osoby.zwrocosobywgid(id); return View(); public ActionResult Edit(int id) OsobaModel osoba = _osoby.zwrocosobywgid(id); return View(); b) Następnie dalej modyfikujemy te metody, tak aby otrzymać następujący kod: public ActionResult Index() List t<osobamodel> dane = _osoby.zwrocosoby(); return View(dane); public ActionResult Details(int id) Osob bamodel osoba = _osoby.zwrocosobywgid(id); List<TelefonModel> numerytelefonow = _telefony.pobierztelefonydlaosoby(id); return View( new SzczegolyOsoby Osoba = osoba, NumeryTelefonow = numerytelefonow ); public ActionResult Edit(int id) Osob bamodel osoba = _osoby.zwrocosobywgid(id); return View(osoba); W kodzie tym należy zwrócić uwagę na następujące elementy: ❶ przekazanie obiektów modelu następuje nie poprzez słownik ViewData, lecz bezpośrednio do widoku (z wykorzystaniem odpowiedniej, przeciążonej wersji metody View(). Pozwoli to na bezpośrednie odwoływanie się do obiektu (czy jak w tym przypadku kolekcji obiektów) w widoku. 3
❷ W przypadku metody Details chcemy, aby na jednym formularzu wyświetlały się jednocześnie informacje o osobie oraz jej numery telefonów. Ponieważ do widoku można przekazać tylko jeden obiekt, konieczne jest utworzenie nowej klasy (w tym przypadku SzczegolyOsoby), która będzie łączyła informacje o osobie z jej numerami telefonów. Ustawiając odpowiednie właściwości tej metody jesteśmy wstanie przekazać do widoku pojedynczy obiekt zawierający wszystkie wymagane informacje. Powyższy kod oczywiście się nie skompiluje ponieważ nie istnieje klasa SzczegolyOsoby. Aby to zmienić do katalogu Models dodajemy nową klasę, która będzie zawierała poniższy kod: public classs SzczegolyOsoby public OsobaModel Osoba get; set; public List<TelefonModel> NumeryTelefonow get; set; III. Stworzenie widoków dla akcji Index, Create, Edit Po poprawieniu kontrolera, możemy przygotować odpowiednie widoki dla poszczególnych akcji. a) Podobnie jak w poprzednim tygodniu klikamy prawym klawiszem myszy wewnątrz metody Index i wybieramy opcję Add View. b) W wyświetlonym oknie dialogowym ustawiamy opcje jak pokazano na rysunku poniżej: Zaznaczenie pola Create a strongly-typed view oznacza, że chcemy stworzyć widok o ścisłej kontroli typów. Wymaga to wybrania z listy rozwijanej View data class wybrania odpowiedniej klasy modelu w tym przypadku OsobaModel. 4
Dodatkowoo możemy określić czy chcemy, aby Visual Studio automatycznie wygenerował kod widoku za nas (wybierając odpowiedni rodzaj widoku z kolejnej listy rozwijanej), czy też nie (wybierając pozycję Empty ). c) Klikając przycisk Add zostanie dla nas wygenerowany widok, którego kod przedstawiony jest poniżej: <%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<IEnumerable<Lab04.Models.OsobaModel>>" %> <asp:content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server"> Index </asp:content> <asp:content ID="Content2" ContentPlaceHolderID="MainContent" runat="server"> <h2>index</h2> <table> <tr> <th></th> <th>id</th> <th>imie</th> <th>drugieimie</th> <th>nazwisko</th> </tr> <% foreach (var item in Model) %> <tr> <td> <%: Html.ActionLink("Edit", "Edit", new /* id=item.primarykey */ ) %> <%: Html.ActionLink("Details", "Details", new /* id=item.primarykey */ )%> <%: Html.ActionLink("Delete", "Delete", new /* id=item.primarykey */ )%> </td> <td><%: item.id %></td> <td><%: item.imiee %></td> <td><%: item.drugieimie %></td> <td><%: item.nazwisko %></td> </tr> <% %> </table> <p><%: Html.ActionLink("Create New", "Create") %></p> </asp:content> Powyższy kod jest w zasadzie kompletnym kodem naszego widoku, należy jedynie wypełnić pola, które są zaznaczone jako komentarz. W polach tych podajemy jaka wartość ma być przekazana do akcji Edit, Details i Delete jako wartość identyfikująca konkretny rekord. We wszystkich trzech miejscach zamiast komentarza podajemy: id=item.id. 5
d) Jak łatwo zauważyć w widoku tym generowana jest tabela, która w pierwszym wierszu będzie zawierała nagłówki odpowiednich kolumn. Wyświetlane wartości zostały wygenerowane na podstawie nazw właściwości klasy, którą wybraliśmy w oknie dialogowym opisywanym w punkcie b). Jednak czasem będziemy chcieli, aby wartości te były inne niż automatycznie wygenerowane (np. w przypadku kolumny DrugieImie było by dobrze, aby oba wyrazyy były napisane osobno i bez błędu. Niestety zmianę tę należy wykonać ręcznie. Dodatkowo można usunąć kolumnę ID, gdyż wartość ta nie ma znaczenia dla użytkownika. e) Warto również zwrócić uwagę na wykorzystaną w tym widoku metodę Html.ActionLink. Jest to jedna z tzw. Helperów, czyli metod, których zadaniem jest uproszczenie generowania odpowiedniego kodu HTML, w tym przypadku odnośnika do odpowiedniej akcji w naszej aplikacji. W dalszej części zajęć spotkamy się jeszcze z kilkoma metodami z tej klasy, jednak proszę o bardziej szczegółowe zapoznanie się z tymi metodami na podstawie dokumentacji lub informacji w Internecie. f) Zanim stworzymy widoki dla akcji Create i Edit dokonajmy pewnej zmiany klasy OsobaModel zgodnie z kodem przedstawionym poniżej: public class OsobaModel publi ic int ID get; set; public string Imie get; set; [DisplayName("Drugie imię")] public string DrugieImie get; set; public string Nazwisko get; set; Zmiana ta pozwoli, aby w kolejno utworzonych widokach, dla właściwości DrugieImiee etykieta była generowana nie na podstawie jej nazwy, tylko na podstawie tekstu podanego w atrybucie DisplayName. Atrybut ten wymaga dołączenia do pliku z klasą OsobaModel przestrzeni nazw: System.ComponentModel. g) Tworzymy dwa nowe widoki odpowiednio dla akcji Create i Edit. W oknie dialogowym również zaznaczamy opcję Create a strongly-typed view, jako model wybieramyy klasę OsobaModel, a na liście rozwijanej View content zaznaczamy opcję odpowiadającą tworzonemu widokowi. h) Jeżeli przyjrzymy się jaki kod został dla nas wygenerowany zobaczymy, cały zestaw metod z klasy Html, które mają pomóc w generowaniu formularza na podstawie przekazanego obiektu. Jakie funkcje pełnią poszczególne metody można się łatwo domyślić na podstawie ich nazw. i) Z obu tych widoków usuwamy kod dotyczący zmian parametru ID: <div class="editor-label"> <%: Html.LabelFor(model => model.id) %> </div> <div class="editor-field"> <%: Html.TextBoxFor(model => model.id) %> <%: Html.ValidationMessageFor(model => model.id) %> </div> 6
IV. Stworzenie widoku dla akcji Details Tworzenie widoku dla akcji Details będzie się trochę różniło od poprzednio utworzonych widoków. Spowodowane jest to faktem wykorzystania klasy modelu, którego właściwości nie są typu prostego, jak w poprzednich punktach, lecz typów złożonych. a) Klikamy prawym przyciskiem myszy wewnątrz metody Details i z menu kontekstowego wybieramy opcję Add View. b) W wyświetlonym oknie dialogowym zaznaczamy opcję Create a strongly-typed view, jako model wybieramy klasę SzczegolyOsoby, a na liście rozwijanej View content zaznaczamy opcję Details. c) Po kliknięciu przycisku Add, wygenerowany zostanie dla nas widok, lecz jak łatwo można zauważyć nie będzie on wyświetlał żadnych użytecznych informacji. Jest to spowodowane faktem, że ASP.NET MVC nie potrafi wygenerować widoku na podstawie modelu złożonego. Fakt ten wykorzystamyy jednak do przedstawienia sposobu tworzenia widoków częściowych i szablonów. d) Najpierw stwórzmy szablon wyświetlania danych dla klasy OsobaModel. W tym celu otwórzmy okno Solution Explorer i w katalogu Views/Shared stwórzmy nowy katalog: DisplayTemplates. e) Klikamy na tym katalogu prawym przyciskiem myszy i wybieramy opcję Add>View. f) W wyświetlonym oknie dialogowym wybieramy opcje tak jak pokazano na rysunku poniżej. Ważne jest aby nazwa widoku była taka samaa jak nazwa klasy modelu na podstawie, której będzie on wygenerowany. g) Po kliknięciu przycisku Add wygenerowany zostanie dla nas widok częściowy o następującym kodzie: 7
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Lab04.Models.OsobaModel>" %> <fieldset> <legend>fields</legend> <div class="display-label">id</div> <div class="display-field"><%: Model.ID %></div> <div class="display-label">imie</div> <div class="display-field"><%: Model.Imie %></div> <div class="display-label">drugieimie</div> <div class="display-field"><%: Model.DrugieImiee %></div> <div class="display-label">nazwisko</div> <div class="display-field"><%: Model.Nazwisko %></div> </fieldset> <p> <%: Htm ml.actionlink("edit", "Edit", new /*id=model.primarykey*/ ) %> <%: Html.ActionLink("Back to List", "Index") %> </p> h) Z wygenerowanego kodu usuwamy następujące linie: <div class="display-label">id</div> <div class="display-field"><%: Model.ID %></div> W tym przypadku, podobnie jak przy poprzednich widokach nie chcemy wyświetlać użytkownikowi identyfikatora. <p> <%: Html.ActionLink("Edit", "Edit", new /*id=model.primarykey*/ ) %> <%: Html.ActionLink("Back to List", "Index") %> </p> W tym przypadku nie chcemy dawać możliwości przejścia do edycji osoby z tego widoku (no chyba, że ktoś chce, to wtedy należy zmienić fragment zaznaczony jako komentarz, tak jak to robiliśmy wcześniej). Odnośnik do listy osób również nie jest nam w tym szablonie potrzebny, ponieważ taki odnośnik będzie znajdował się bezpośrednio na stronie, na której ten szablon będzie wykorzystany. i) Stwórzmy teraz widok częściowy, który będzie wyświetlał numery telefonów użytkownika. W tym celu w oknie Solution Explorer klikamy prawym przyciskiem myszy na katalogu View/Shared i wybieramy opcję Add>View. j) W oknie dialogowym ustawiamy odpowiednie opcje zgodnie z rysunkiem przedstawionym poniżej: 8
k) Po kliknięciu przycisku Add wygenerowany zostanie dla nas widok częściowy, z którego wyrzucamy kolumnę ID oraz uzupełniamy kod metod Html.ActionLink, podobnie jak przy tworzeniu widoku dla akcji Index. l) Gdy mamy już wygenerowane odpowiednie szablony i widoki częściowe możemy wrócić do naszego widoku Details i zmodyfikować go zgodnie z kodem przedstawionym poniżej: <%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<Lab04.Models.SzczegolyOsoby>" %> <asp:content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server" "> Details </asp:content> <asp:content ID="Content2" ContentPlaceHolderID="MainContent" runat="server"> <h2>szczegóły</h2> <%: Html.DisplayFor(x=>x.Osoba) %> <fieldset> <legend>numery telefonów:</legend> <%: Html.Partial( "NumeryTelefonow", Model.NumeryTelefonow) %> </fieldset> <p> <%: Html.ActionLink("Back to List", "Index") </p> %> </asp:content> Należy zwrócić uwagę na wykorzystanie w kodzie dwóch metod z klasy Html: DisplayFor oraz Partial. Metoda DisplayFor służy do wyświetlenia szablonu dla właściwości przekazanej jako parametr. Metoda ta na podstawie typu właściwości przekazanej do funkcji jako parametr decyduje, który szablon wybrać, dlatego konieczne było nazwanie szablonu dokładnie tak samo jak klasy, dla której był wygenerowany. 9
Metoda Partial służy do wstawienia w miejsce wywołania kodu widoku częściowego. Wykorzystana wersja tej metody, jako pierwszy parametr przyjmuje nazwę widoku częściowego, a jako drugi obiekt, który ma dostarczyć dane konieczne do jego wygenerowania. V. Zadania do samodzielnego wykonania 1. Proszę zaprogramować brakujące akcje kontrolera i silnie typowane widoki. a. W przypadku widoku dla akcji Delete, kontrolera OsobaController można posłużyć się stworzonym szablonem. b. Ponieważ dodawanie, usuwanie i edycja numerów telefonów będą obsługiwane przez kontroler TelefonController, konieczne będzie zmodyfikowanie utworzonego widoku częściowego. Modyfikacji wymaga metoda Html.ActionLink, która powinna wskazywać na odpowiedni kontroler (TelegonController), a domyślnie generuje ona link do kontrolera, z którego wywołany został widok. W celu wygenerowania poprawnego odnośnika należy wykorzystać inną wersję tej metody, która przyjmuje 5 parametrów: Generowany tekst odnośnika (typu string) Nazwę akcji (typu string) Nazwę kontrolera (typu string) Parametry przekazane przez link (typu object) Atrybuty HTML, które mają być ustawione dla odnośnika (typu object). W naszym przypadku wywołanie może mieć następującą postać: Html.ActionLink( Edit, Edit, TelefonController, new id = item.id, null); 10