Ponowne używanie interfejsu użytkownika za pomocą stron wzorcowych i częściowych

autor: Microsoft

Pobierz plik PDF

Jest to krok 7 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 7 analizuje sposoby stosowania reguły "DRY" w szablonach widoków w celu wyeliminowania duplikowania kodu przy użyciu szablonów widoku częściowego i stron wzorcowych.

Jeśli używasz ASP.NET MVC 3, zalecamy skorzystanie z samouczków Wprowadzenie With MVC 3 lub MVC Music Store.

NerdDinner — krok 7. Częściowe i strony wzorcowe

Jedną z filozofii projektowania, ASP.NET MVC obejmuje zasadę "Nie powtarzaj siebie" (często nazywaną "DRY"). Projekt DRY pomaga wyeliminować duplikowanie kodu i logiki, co ostatecznie sprawia, że aplikacje są szybsze w tworzeniu i łatwiejszej konserwacji.

Widzieliśmy już zasadę DRY zastosowaną w kilku naszych scenariuszach NerdDinner. Kilka przykładów: nasza logika walidacji jest implementowana w naszej warstwie modelu, co umożliwia jej wymuszanie zarówno w scenariuszach edycji, jak i tworzenia w naszym kontrolerze; Ponownie używamy szablonu widoku "NotFound" w metodach akcji Edytuj, Szczegóły i Usuń; Używamy wzorca nazewnictwa konwencji z naszymi szablonami widoków, co eliminuje konieczność jawnego określenia nazwy podczas wywoływania metody pomocnika View(). i ponownie używamy klasy DinnerFormViewModel dla scenariuszy akcji Edytuj i Utwórz.

Przyjrzyjmy się teraz sposobom stosowania reguły "DRY" w naszych szablonach widoków, aby wyeliminować tam również duplikowanie kodu.

Ponowne odwiedzanie szablonów edycji i tworzenia widoków

Obecnie do wyświetlania interfejsu użytkownika formularza kolacji używamy dwóch różnych szablonów widoków : "Edit.aspx" i "Create.aspx". Krótkie porównanie wizualne pokazuje, jak podobne są. Poniżej przedstawiono wygląd formularza tworzenia:

Zrzut ekranu przedstawiający stronę Moja aplikacja języka C W języku M. Zostanie wyświetlony formularz Hostowanie kolacji.

Oto jak wygląda nasz formularz "Edytuj":

Zrzut ekranu przedstawiający paage aplikacji My M V C. Zostanie wyświetlony formularz Edycja.

Nie ma wiele różnicy? Poza tekstem tytułu i nagłówka układ formularza i kontrolki wprowadzania są identyczne.

Jeśli otworzymy szablony widoków "Edit.aspx" i "Create.aspx", przekonamy się, że zawierają one identyczny układ formularza i kod sterowania danymi wejściowymi. To duplikowanie oznacza, że w końcu musimy dwukrotnie wprowadzać zmiany lub zmieniać nową właściwość Dinner — co nie jest dobre.

Korzystanie z szablonów widoku częściowego

ASP.NET MVC obsługuje możliwość definiowania szablonów "widoku częściowego", których można użyć do hermetyzacji logiki renderowania widoku dla części podrzędnej strony. "Częściowe" zapewniają przydatny sposób definiowania logiki renderowania raz, a następnie ponownego użycia w wielu miejscach w aplikacji.

Aby ułatwić "DRY-up" nasze pliki Edit.aspx i Create.aspx wyświetl duplikowanie szablonów, możemy utworzyć szablon widoku częściowego o nazwie "DinnerForm.ascx", który hermetyzuje układ formularza i elementy wejściowe wspólne dla obu tych elementów. Zrobimy to, klikając prawym przyciskiem myszy katalog /Views/Dinners i wybierając polecenie menu "Dodaj widok>":

Zrzut ekranu przedstawiający drzewo nawigacji Eksplorator rozwiązań. Kolacja jest wyróżniona. Wybrano pozycję Dodaj i Wyświetl. Widok jest wyróżniony.

Spowoduje to wyświetlenie okna dialogowego "Dodaj widok". Nadamy nowemu widokowi nazwę "DinnerForm", zaznaczymy pole wyboru "Utwórz widok częściowy" w oknie dialogowym i wskażemy, że przekażemy mu klasę DinnerFormViewModel:

Zrzut ekranu przedstawiający okno dialogowe Dodawanie widoku. Opcja Utwórz widok częściowy jest zaznaczona i wyróżniona.

Po kliknięciu przycisku "Dodaj" program Visual Studio utworzy nowy szablon widoku "DinnerForm.ascx" dla nas w katalogu "\Views\Dinners".

Następnie możemy skopiować/wkleić zduplikowany układ formularza/kod sterujący danych wejściowych z szablonów widoku Edit.aspx/ Create.aspx do naszego nowego szablonu widoku częściowego "DinnerForm.ascx":

<%= Html.ValidationSummary("Please correct the errors and try again.") %>

<% using (Html.BeginForm()) { %>

    <fieldset>
        <p>
            <label for="Title">Dinner Title:</label>
            <%= Html.TextBox("Title", Model.Dinner.Title) %>
            <%=Html.ValidationMessage("Title", "*") %>
        </p>
        <p>
            <label for="EventDate">Event Date:</label>
            <%= Html.TextBox("EventDate", Model.Dinner.EventDate) %>
            <%= Html.ValidationMessage("EventDate", "*") %>
        </p>
        <p>
            <label for="Description">Description:</label>
            <%= Html.TextArea("Description", Model.Dinner.Description) %>
            <%= Html.ValidationMessage("Description", "*") %>
        </p>
        <p>
            <label for="Address">Address:</label>
            <%= Html.TextBox("Address", Model.Dinner.Address) %>
            <%= Html.ValidationMessage("Address", "*") %>
        </p>
        <p>
            <label for="Country">Country:</label>
            <%= Html.DropDownList("Country", Model.Countries) %>                
            <%= Html.ValidationMessage("Country", "*") %>
        </p>
        <p>
            <label for="ContactPhone">Contact Phone #:</label>
            <%= Html.TextBox("ContactPhone", Model.Dinner.ContactPhone) %>
            <%= Html.ValidationMessage("ContactPhone", "*") %>
        </p>
            
        <p>
            <input type="submit" value="Save"/>
        </p>
    </fieldset>
    
<% } %>

Następnie możemy zaktualizować nasze szablony widoku Edytuj i Utwórz, aby wywołać szablon częściowy DinnerForm i wyeliminować duplikowanie formularza. Możemy to zrobić, wywołując element Html.RenderPartial("DinnerForm") w szablonach widoków:

Create.aspx
<asp:Content ID="Title" ContentPlaceHolderID="TitleContent" runat="server">
    Host a Dinner
</asp:Content>

<asp:Content ID="Create" ContentPlaceHolderID="MainContent" runat="server">

    <h2>Host a Dinner</h2>

    <% Html.RenderPartial("DinnerForm"); %>
    
</asp:Content>
Edit.aspx
<asp:Content ID="Title" ContentPlaceHolderID="TitleContent" runat="server">
    Edit: <%=Html.Encode(Model.Dinner.Title) %>
</asp:Content>

<asp:Content ID="Edit" ContentPlaceHolderID="MainContent" runat="server">

    <h2>Edit Dinner</h2>

    <% Html.RenderPartial("DinnerForm"); %>

</asp:Content>

Możesz jawnie zakwalifikować ścieżkę szablonu częściowego, który ma zostać wywołany podczas wywoływania pliku Html.RenderPartial (na przykład: ~Views/Dinners/DinnerForm.ascx). W powyższym kodzie korzystamy jednak ze wzorca nazewnictwa opartego na konwencji w ASP.NET MVC i po prostu określamy wartość "DinnerForm" jako nazwę częściowego renderowania. Jeśli to zrobimy, ASP.NET MVC będzie wyglądać najpierw w katalogu widoków opartych na konwencji (w przypadku dinnersController będzie to /Views/Dinners). Jeśli nie znajdzie szablonu częściowego, poszukaj go w katalogu /Views/Shared.

Gdy element Html.RenderPartial() jest wywoływany przy użyciu tylko nazwy widoku częściowego, ASP.NET MVC przekaże do widoku częściowego te same obiekty słownika Model i ViewData używane przez szablon widoku wywołującego. Alternatywnie istnieją przeciążone wersje html.RenderPartial(), które umożliwiają przekazanie alternatywnego obiektu modelu i/lub słownika ViewData dla widoku częściowego do użycia. Jest to przydatne w scenariuszach, w których chcesz przekazać tylko podzbiór pełnego modelu/modelu ViewModel.

Temat boczny: Dlaczego <% %> zamiast <%= %>?
Jedną z subtelnych rzeczy, które można zauważyć w powyższym kodzie, jest użycie <bloku % %> zamiast <bloku %= %> podczas wywoływania metody Html.RenderPartial(). <Bloki %= %> w ASP.NET wskazują, że deweloper chce renderować określoną wartość (na przykład: <%= "Hello" %> renderuje wartość "Hello"). <Bloki % %> zamiast tego wskazują, że deweloper chce wykonać kod i że wszystkie renderowane dane wyjściowe w nich muszą być wykonywane jawnie (na przykład: <% Response.Write("Hello") %>. Przyczyną <użycia bloku % %> z powyższym kodem HTML.RenderPartial jest to, że metoda Html.RenderPartial() nie zwraca ciągu, a zamiast tego zwraca zawartość bezpośrednio do strumienia wyjściowego szablonu widoku wywołującego. Robi to ze względu na wydajność i dzięki temu pozwala uniknąć konieczności utworzenia (potencjalnie bardzo dużego) tymczasowego obiektu ciągu. Zmniejsza to użycie pamięci i zwiększa ogólną przepływność aplikacji. Jednym z typowych błędów podczas korzystania z funkcji Html.RenderPartial() jest zapomnienie dodania średnika na końcu wywołania, gdy znajduje się w <bloku % %> . Na przykład ten kod spowoduje błąd kompilatora: <% Html.RenderPartial("DinnerForm") %> Zamiast tego musisz napisać: <% Html.RenderPartial("DinnerForm"); %> jest to spowodowane tym, że <> % bloków są instrukcjami kodu samodzielnego, a w przypadku korzystania z instrukcji kodu języka C# należy zakończyć średnikiem.

Używanie szablonów widoku częściowego do wyjaśnienia kodu

Utworzyliśmy szablon widoku częściowego "DinnerForm", aby uniknąć duplikowania logiki renderowania widoku w wielu miejscach. Jest to najczęstsza przyczyna tworzenia szablonów widoku częściowego.

Czasami warto tworzyć widoki częściowe nawet wtedy, gdy są wywoływane tylko w jednym miejscu. Bardzo skomplikowane szablony widoków często stają się znacznie łatwiejsze do odczytania, gdy ich logika renderowania widoku jest wyodrębniona i podzielona na co najmniej jeden dobrze nazwany szablon częściowy.

Rozważmy na przykład poniższy fragment kodu z pliku Site.master w naszym projekcie (który wkrótce omówimy). Kod jest stosunkowo prosty do odczytania — częściowo dlatego, że logika wyświetlania linku logowania/wylogowywania w prawym górnym rogu ekranu jest hermetyzowana w części "LogOnUserControl":

<div id="header">
    <div id="title">
        <h1>My MVC Application</h1>
    </div>
      
    <div id="logindisplay">
        <% Html.RenderPartial("LogOnUserControl"); %>
    </div> 
    
    <div id="menucontainer">
    
        <ul id="menu">              
            <li><%=Html.ActionLink("Home", "Index", "Home")%></li>
            <li><%=Html.ActionLink("About", "About", "Home")%></li>
        </ul>
    </div>
</div>

Za każdym razem, gdy próbujesz zrozumieć znaczniki html/code w szablonie widoku, zastanów się, czy nie byłoby bardziej jasne, czy niektóre z nich zostały wyodrębnione i refaktoryzowane do dobrze nazwanych widoków częściowych.

Strony wzorcowe

Oprócz obsługi widoków częściowych ASP.NET MVC obsługuje również możliwość tworzenia szablonów "strony wzorcowej", które mogą służyć do definiowania wspólnego układu i kodu HTML najwyższego poziomu witryny. Kontrolki symbolu zastępczego zawartości można następnie dodać do strony wzorcowej, aby zidentyfikować regiony, które można zastąpić lub "wypełnić" widokami. Zapewnia to bardzo skuteczny (i DRY) sposób zastosowania wspólnego układu w aplikacji.

Domyślnie nowe projekty MVC ASP.NET mają do nich automatycznie dodany szablon strony wzorcowej. Ta strona wzorcowa nosi nazwę "Site.master" i znajduje się w folderze \Views\Shared\:

Zrzut ekranu przedstawiający drzewo nawigacji Nerd Dinner. Wzorzec witryny jest wyróżniony i zaznaczony.

Domyślny plik Site.master wygląda następująco. Definiuje zewnętrzny kod HTML witryny wraz z menu nawigacji u góry. Zawiera on dwie zamienialne kontrolki symboli zastępczych zawartości — jeden dla tytułu, a drugi dla miejsca, w którym należy zastąpić podstawową zawartość strony:

<%@ Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">

<head runat="server">
    <title>
       <asp:ContentPlaceHolder ID="TitleContent" runat="server" />
    </title>
   <link href="../../Content/Site.css" rel="stylesheet" type="text/css" />
</head>

<body>
    <div class="page">

        <div id="header">
            <div id="title">
                <h1>My MVC Application</h1>
            </div>
              
            <div id="logindisplay">
                <% Html.RenderPartial("LogOnUserControl"); %>
            </div> 
            
            <div id="menucontainer">

                <ul id="menu">              
                    <li><%=Html.ActionLink("Home", "Index", "Home")%></li>
                    <li><%=Html.ActionLink("About", "About", "Home")%></li>
                </ul>
            
            </div>
        </div>

        <div id="main">
            <asp:ContentPlaceHolder ID="MainContent" runat="server" />
        </div>
    </div>
</body>
</html>

Wszystkie szablony widoków utworzone dla aplikacji NerdDinner ("List", "Details", "Edit", "Create", "NotFound" itp.) zostały oparte na tym szablonie Site.master. Jest to wskazane za pomocą atrybutu "MasterPageFile", który został dodany domyślnie do górnej <dyrektywy % @ Page %> podczas tworzenia naszych widoków przy użyciu okna dialogowego "Dodaj widok":

<%@ Page Inherits="System.Web.Mvc.ViewPage<NerdDinner.Controllers.DinnerViewModel>" MasterPageFile="~/Views/Shared/Site.Master" %>

Oznacza to, że możemy zmienić zawartość Site.master i automatycznie zastosować zmiany i używać ich podczas renderowania dowolnego z naszych szablonów widoków.

Zaktualizujmy sekcję nagłówka site.master, tak aby nagłówek naszej aplikacji był "NerdDinner" zamiast "Moja aplikacja MVC". Zaktualizujmy również nasze menu nawigacji, tak aby pierwsza karta to "Znajdź kolację" (obsługiwana przez metodę akcji Index() HomeControllera i dodamy nową kartę o nazwie "Host a Dinner" (obsługiwana przez metodę akcji Create() DinnersController):

<div id="header">

    <div id="title">
        <h1>NerdDinner</h1>
    </div>

    <div id="logindisplay">
        <% Html.RenderPartial("LoginStatus"); %>
    </div> 
    
    <div id="menucontainer">
        <ul id="menu">      
           <li><%=Html.ActionLink("Find Dinner", "Index", "Home")%></li>
           <li><%=Html.ActionLink("Host Dinner", "Create", "Dinners")%></li>
           <li><%=Html.ActionLink("About", "About", "Home")%></li>   
        </ul>
    </div>
</div>

Po zapisaniu pliku Site.master i odświeżeniu przeglądarki zobaczymy, że zmiany nagłówka będą wyświetlane we wszystkich widokach w naszej aplikacji. Na przykład:

Zrzut ekranu przedstawiający stronę listy Nerd Dinner Upcoming Dinners (Nadchodzące kolacje).

I przy użyciu adresu URL /Dinners/Edit/[id] :

Zrzut ekranu przedstawiający stronę formularza edycji kolacji Nerd.

Następny krok

Częściowe i strony wzorcowe zapewniają bardzo elastyczne opcje, które umożliwiają czyste organizowanie widoków. Przekonasz się, że ułatwiają one unikanie duplikowania zawartości/kodu widoku i ułatwiają odczytywanie i konserwowanie szablonów widoków.

Wróćmy teraz do scenariusza wyświetlania listy, który utworzyliśmy wcześniej i włączymy skalowalną obsługę stronicowania.