Поделиться через


Многократное использование пользовательского интерфейса с помощью эталонных страниц и частичных представлений

от Майкрософт

Загрузить PDF-файл

Это шаг 7 бесплатного руководства по приложению NerdDinner , в рамках которых показано, как создать небольшое, но полное веб-приложение с помощью ASP.NET MVC 1.

На шаге 7 рассматриваются способы применения принципа DRY в шаблонах представлений, чтобы исключить дублирование кода с помощью шаблонов частичных представлений и master страниц.

Если вы используете ASP.NET MVC 3, рекомендуется следовать руководствам по начало работы С MVC 3 или MVC Music Store.

NerdDinner, шаг 7. Частичная и эталонная страницы

Одной из философий проектирования, ASP.NET MVC, является принцип "Не повторяйся" (обычно называется "DRY"). Проектирование DRY помогает избежать дублирования кода и логики, что в конечном итоге ускоряет сборку приложений и упрощает их обслуживание.

Мы уже видели, как принцип DRY применяется в нескольких сценариях NerdDinner. Несколько примеров: логика проверки реализована на уровне модели, что позволяет применять ее в сценариях редактирования и создания в контроллере; мы повторно используем шаблон представления NotFound в методах действия Изменить, Сведения и Удалить; мы используем шаблон именования соглашений с нашими шаблонами представлений, что избавляет от необходимости явно указывать имя при вызове вспомогательного метода View(); и мы повторно используем класс DinnerFormViewModel для сценариев действий "Изменить" и "Создать".

Теперь давайте рассмотрим способы применения "принципа DRY" в наших шаблонах представлений, чтобы исключить дублирование кода.

Повторное посещение шаблонов "Изменение и создание представлений"

В настоящее время мы используем два разных шаблона представлений — Edit.aspx и Create.aspx — для отображения пользовательского интерфейса формы dinner. Быстрое визуальное сравнение показывает, насколько они похожи. Ниже показано, как выглядит форма создания.

Снимок экрана: страница приложения My M V C. Отображается форма

А вот как выглядит форма "Изменить":

Снимок экрана: страница приложения My M V C. Отображается форма редактирования.

Нет большой разницы? Кроме текста заголовка и заголовка, макет формы и элементы управления входными данными идентичны.

Если открыть шаблоны представлений Edit.aspx и Create.aspx, мы увидим, что они содержат идентичный макет формы и код элемента управления входными данными. Это дублирование означает, что нам в конечном итоге приходится вносить изменения дважды каждый раз, когда мы вводим или меняем новое свойство Dinner - что не хорошо.

Использование шаблонов частичных представлений

ASP.NET MVC поддерживает возможность определения шаблонов "частичного представления", которые можно использовать для инкапсуляции логики отрисовки представления для подчасти страницы. "Частичная часть" предоставляет полезный способ определения логики отрисовки представления один раз, а затем повторного использования ее в нескольких местах в приложении.

Чтобы упростить дублирование шаблонов представлений Edit.aspx и Create.aspx, мы можем создать шаблон частичного представления с именем DinnerForm.ascx, который инкапсулирует макет формы и входные элементы, общие для обоих. Для этого щелкните правой кнопкой мыши каталог /Views/Dinners и выберите команду меню "Add-View>":

Снимок экрана: дерево навигации Обозреватель решений. Ужин выделен. Выбраны пункты Добавить и Просмотреть. Представление выделено.

Откроется диалоговое окно "Добавить представление". Мы присвоим новому представлению имя "DinnerForm", установите флажок "Создать частичное представление" в диалоговом окне и укажем, что передадим его класс DinnerFormViewModel:

Снимок экрана: диалоговое окно добавления представления. Выбрано и выделено создание частичного представления.

При нажатии кнопки "Добавить" Visual Studio создаст для нас новый шаблон представления "DinnerForm.ascx" в каталоге \Views\Dinners.

Затем мы можем скопировать или вставить дубликат макета формы или ввод кода элемента управления из наших шаблонов представления Edit.aspx/ Create.aspx в наш новый шаблон частичного представления "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>
    
<% } %>

Затем мы можем обновить шаблоны представления "Изменить" и "Создать", чтобы вызвать частичный шаблон DinnerForm и исключить дублирование формы. Это можно сделать, вызвав Html.RenderPartial("DinnerForm") в наших шаблонах представлений:

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>

Вы можете явно указать путь к частичному шаблону при вызове Html.RenderPartial (например, ~Views/Dinners/DinnerForm.ascx). Однако в приведенном выше коде мы пользуемся шаблоном именования на основе соглашений в ASP.NET MVC и просто указываем "DinnerForm" в качестве имени части для отрисовки. При этом ASP.NET MVC будет выглядеть первым в каталоге представлений на основе соглашений (для DinnersController это будет /Views/Dinners). Если частичный шаблон отсутствует, он будет искать его в каталоге /Views/Shared.

При вызове Html.RenderPartial() только с именем частичного представления ASP.NET MVC передает в частичное представление те же объекты словаря Model и ViewData, которые используются шаблоном вызывающего представления. Кроме того, существуют перегруженные версии Html.RenderPartial(), которые позволяют передавать альтернативный объект Model и/или словарь ViewData для использования частичного представления. Это полезно для сценариев, в которых требуется передать только подмножество полной модели или модели представления.

Side Topic: Why <%> вместо <%= %>?
Одна из тонких вещей, которые вы могли заметить в приведенном выше коде, заключается в том, что при вызове Html.RenderPartial() мы используем <блок %>% вместо <%= %.> <%= %> блоков в ASP.NET указывает, что разработчик хочет отобразить указанное значение (например, <%= "Hello" %> отрисовывает "Hello"). <Вместо этого блоки %> указывают на то, что разработчик хочет выполнить код и что все отображаемые выходные данные в них должны выполняться явным образом (например: <% Response.Write("Hello") %>. Причина, по которой мы используем <блок %> в приведенном выше коде Html.RenderPartial, заключается в том, что метод Html.RenderPartial() не возвращает строку и вместо этого выводит содержимое непосредственно в выходной поток шаблона вызывающего представления. Это делается из соображений повышения производительности и, таким образом, позволяет избежать необходимости создания (потенциально очень большого) временного объекта строки. Это сокращает использование памяти и повышает общую пропускную способность приложения. Одна из распространенных ошибок при использовании Html.RenderPartial() — забыть добавить точку с запятой в конце вызова, когда она находится в % <> блока. Например, этот код вызовет ошибку компилятора: <% Html.RenderPartial("DinnerForm") %> Вместо этого необходимо написать: <% Html.RenderPartial("DinnerForm"); %> Это связано с тем, что <%> блоков являются автономными операторами кода, а при использовании операторов кода C# необходимо завершить точкой с запятой.

Использование шаблонов частичных представлений для уточнения кода

Мы создали шаблон частичного представления DinnerForm, чтобы избежать дублирования логики отрисовки представлений в нескольких местах. Это наиболее распространенная причина создания шаблонов частичных представлений.

Иногда по-прежнему имеет смысл создавать частичные представления, даже если они вызываются только в одном месте. Очень сложные шаблоны представлений часто становятся гораздо проще читать, когда логика отрисовки представлений извлекается и секционируется на один или несколько частично именованных шаблонов.

Например, рассмотрим приведенный ниже фрагмент кода с сайта. master файл в нашем проекте (который мы рассмотрим в ближайшее время). Код относительно прямой для чтения, отчасти потому, что логика отображения ссылки для входа или выхода в правом верхнем углу экрана инкапсулирована в части 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>

Всякий раз, когда вы путаетесь при попытке понять разметку HTML или кода в шаблоне представления, подумайте, не будет ли она более ясной, если часть из нее была извлечена и рефакторингована в хорошо называемые частичные представления.

Эталонные страницы

Помимо поддержки частичных представлений, ASP.NET MVC также поддерживает возможность создания шаблонов "master страницы", которые можно использовать для определения общего макета и HTML верхнего уровня сайта. Элементы управления заполнителями содержимого затем можно добавить на страницу master, чтобы определить заменяемые области, которые могут быть переопределены или заполнены представлениями. Это обеспечивает очень эффективный (и DRY) способ применения общего макета в приложении.

По умолчанию в новые проекты ASP.NET MVC автоматически добавляется шаблон страницы master. Эта страница master называется Site.master и находится в папке \Views\Shared\:

Снимок экрана: дерево навигации Nerd Dinner. Мастер сайта выделен и выбран.

Сайт по умолчанию. master файл выглядит, как показано ниже. Он определяет внешний HTML сайта, а также меню для навигации в верхней части. Он содержит два заменяемых заполнителя содержимого: один для заголовка, а другой — для замены основного содержимого страницы:

<%@ 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>

Все шаблоны представлений, созданные для приложения NerdDinner ("List", "Details", "Edit", "Create", "NotFound" и т. д.), основаны на этом сайте. master шаблон. Это указывается с помощью атрибута MasterPageFile, который был добавлен по умолчанию в верхнюю <директиву % @ Page %> при создании представлений с помощью диалогового окна "Добавить представление":

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

Это означает, что мы можем изменить сайт. master содержимого, и изменения будут автоматически применены и использованы при отрисовки любого из наших шаблонов представлений.

Давайте обновим наш сайт. master раздел заголовка, чтобы заголовок нашего приложения был "NerdDinner" вместо "My MVC Application". Давайте также обновим наше меню навигации так, чтобы первая вкладка была "Найти ужин" (обрабатывается методом действия Index() HomeController), и добавим новую вкладку с именем "Размещение ужина" (обрабатывается методом действия 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>

При сохранении сайта. master файл и обновить браузер, мы увидим, как изменения заголовков отображаются во всех представлениях в приложении. Пример:

Снимок экрана: страница списка предстоящих ужинов Nerd Dinners.

И с URL-адресом /Dinners/Edit/[id] :

Снимок экрана: страница формы редактирования Nerd Dinner.

Следующий шаг

Частичные и master страницы предоставляют очень гибкие возможности, позволяющие четко упорядочивать представления. Вы обнаружите, что они помогают избежать дублирования содержимого или кода представления, а также упрощают чтение и обслуживание шаблонов представлений.

Теперь давайте вернемся к сценарию листинга, который мы создали ранее, и включим поддержку масштабируемого разбиения по страницам.