Разделы компоновки
108ASP.NET --- ASP.NET MVC 5 --- Разделы компоновки
Механизм Razor поддерживает концепцию разделов, которые позволяют организовывать области содержимого внутри компоновки. Разделы Razor предоставляют больший контроль над тем, какие части представления вставляются в компоновку, и куда они помещаются. Для демонстрации работы разделов файл /Views/Home/Index.cshtml проекта, который мы начали в предыдущей статье, отредактирован, как показано в примере ниже:
@model string[]
@{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
}
@section Header {
<div class="view">
@foreach (string str in new[] { "Home", "List", "Edit" })
{
@Html.ActionLink(str, str, null, new { style = "margin: 5px" })
}
</div>
}
<div class="view">
<h2>Названия фруктов</h2>
@foreach (string fruit in Model)
{
<br /><span><b>@fruit</b></span>
}
</div>
@section Footer {
<div class="view">
Это футер
</div>
}
Разделы определяются с помощью Razor-дескриптора @section, за которым следует имя раздела. В этом примере созданы разделы с именами Header и Footer. Раздел содержит обычную смесь разметки HTML и дескрипторов Razor. Разделы определяются в представлении, но применяются в компоновке посредством вспомогательного метода RenderSection(). Чтобы продемонстрировать это в работе, был создан файл /Views/Shared/_Layout.cshtml, содержимое которого приведено в примере ниже:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>@ViewBag.Title</title>
<style>
div.layout { background-color: lightblue; }
div.view { border: 1px solid orange; margin: 10px 0; }
</style>
</head>
<body>
@RenderSection("Header")
<div class="layout">
Этот текст находится в компоновке
</div>
@RenderSection("Body")
<div class="layout">
Этот текст находится в компоновке
</div>
@RenderSection("Footer")
<div class="layout">
Этот текст находится в компоновке
</div>
</body>
</html>
Местоположения для специального механизма визуализации по-прежнему используются, но в файле Index.cshtml представление было указано явно, а это означает, что компоновка будет извлекаться из папки /Views/Shared, хотя разделяемые представления находятся в папке /Views/Common.
Когда Razor осуществляет разбор компоновки, вызов вспомогательного метода RenderSection() заменяется содержимым раздела в представлении с указанным именем. Части представления, которые не содержатся в каком-либо разделе, вставляются в компоновку с использованием вспомогательного метода RenderBody().
Чтобы увидеть эффект от применения разделов, необходимо запустить приложение. В целях прояснения, какие разделы вывода берутся из представления, а какие - из компоновки, было добавлено несколько базовых стилей CSS. Результат не особо впечатляет, но зато четко демонстрирует, как можно помещать области содержимого из представления в специфичные местоположения внутри компоновки:

В представлении можно определять только разделы, на которые имеются ссылки в компоновке. При попытке определить в представлении разделы, для которых отсутствуют соответствующие вызовы вспомогательного метода @RenderSection() в компоновке, MVC Framework сгенерирует исключение.
Смешивание разделов с остальной частью представления обычно не делается. По соглашению все разделы должны определяться либо в начале, либо в конце представления, чтобы упростить выяснение того, какие области содержимого будут трактоваться как разделы, а что будет захвачено вспомогательным методом RenderBody(). Более предпочтительный подход предполагает определение представления полностью в терминах разделов, включая раздел для тела (Body), как показано в примере ниже:
...
@section Body {
<div class="view">
<h2>Названия фруктов</h2>
@foreach (string fruit in Model)
{
<br /><span><b>@fruit</b></span>
}
</div>
}
...
При таком подходе получаются более чистые представления и уменьшаются шансы захвата излишнего содержимого методом RenderBody(). Для применения этого подхода необходимо заменить вызов вспомогательного метода RenderBody() вызовом RenderSection("Body") в компоновке _Layout.cshtml, как показано в примере ниже:
...
@RenderSection("Body")
...
Проверка существования разделов
Внутри компоновки можно выполнить проверку, определен ли в представлении конкретный раздел. Это полезно, когда нужно обеспечить наличие стандартного содержимого для раздела, для которого в представлении ничего не задано. Содержимое файла _Layout.cshtml было изменено для добавления проверки, определен ли раздел Footer:
...
@if (IsSectionDefined("Footer"))
{
@RenderSection("Footer")
} else
{
<h4>Это футер по умолчанию</h4>
}
...
Вспомогательный метод IsSectionDefined() принимает имя проверяемого раздела и возвращает true, если он определен в визуализируемом представлении. В данном примере этот вспомогательный метод используется для выяснения необходимости в генерации стандартного содержимого, когда в представлении не определен раздел Footer.
Визуализация необязательных разделов
По умолчанию представление должно содержать все разделы, для которых имеются вызовы RenderSection() в компоновке. Если какой-то раздел отсутствует, инфраструктура MVC Framework сообщит об этом пользователю, сгенерировав исключение. Чтобы продемонстрировать это, в файл _Layout.cshtml добавляется новый вызов RenderSection() для раздела по имени Scripts. Указанный раздел добавляется в компоновку средой Visual Studio по умолчанию, когда создается проект MVC с применением шаблона MVC.
<!DOCTYPE html>
<html>
<head>
...
</head>
<body>
...
@RenderSection("Scripts")
<div class="layout">
Этот текст находится в компоновке
</div>
</body>
</html>
После запуска приложения механизм Razor попытается визуализировать компоновку и представление, в результате чего возникает ошибка, как показано на рисунке:

Можно воспользоваться методом IsSectionDefined(), чтобы избежать вызова RenderSection() для разделов, которые в представлении не определены, но более элегантный подход предусматривает применение необязательных разделов, для которых методу RenderSection() передается дополнительное значение false:
...
@RenderSection("Scripts", false)
...
Это создает необязательный раздел, содержимое которого будет вставлено в результат, если представление определено, и его отсутствие не приведет к генерации исключения.