Модели и компоновки в Razor

84

Механизм визуализации обрабатывает код ASP.NET и ищет инструкции, которые обычно вставляют динамическое содержимое в вывод, отправляемый браузеру, a Razor - это название механизма визуализации MVC Framework. В версии MVC 5 механизм Razor не изменился, и если вы уже знакомы с его синтаксисом по предшествующим версиям, можете пропустить этот материал.

Мы собираемся начать с простой модели предметной области по имени Product, определенной в файле класса Product.cs, который добавляется в папку Models. Ниже показано содержимое этого нового файла:

namespace Razor.Models 
{
    public class Product 
    {
        public int ProductID { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
        public decimal Price { get; set; }
        public string Category { set; get; }
    }
}

Мы собираемся последовать соглашению MVC Framework и определить в качестве стартовой точки проекта контроллер по имени Home. Щелкните правой кнопкой мыши на папке Controllers в окне Solution Explorer, выберите в контекстном меню пункт Add --> Controller, укажите вариант MVC 5 Controler - в диалоговом окне Add Scaffold и щелкните на кнопке Add. В диалоговом окне Add Controller задайте для имени контроллера HomeController и щелкните на кнопке Add, чтобы создать файл Controllers/HomeController.cs. Приведите содержимое этого файла в соответствие с кодом:

using System.Web.Mvc;
using Razor.Models;

namespace Razor.Controllers {

    public class HomeController : Controller {
        Product myProduct = new Product {
            ProductID = 1,
            Name = "Каяк",
            Description = "Лодка на одного человека",
            Category = "Водные виды спорта",
            Price = 275M
        };

        public ActionResult Index() {
            return View(myProduct);
        }

        public ActionResult NameAndPrice() {
            return View(myProduct);
        }
    }
}

В контроллере Home определен метод действия по имени Index(). Объект Product, созданный в конструкторе, передается методу View(), так что он используется в качестве модели во время визуализации представления. При вызове метода View() имя файла представления не указывается, поэтому будет выбрано стандартное представление для метода действия.

Щелкните правой кнопкой мыши на методе действия Index и выберите в контекстном меню пункт Add View (Добавить представление). Удостоверьтесь, что именем представления является Index, измените Template на Empty и выберите в раскрывающемся списке Model class (Класс модели) вариант Product. (Если вы не видите класса Product в раскрывающемся списке, скомпилируйте проект и попробуйте создать представление заново.) Снимите отметку с флажка View Options (Параметры представления) и щелкните на кнопке Add, чтобы создать файл Index.cshtml в папке Views/Home.

Начальное содержимое этого нового файла показано в примере ниже:

@model Razor.Models.Product

@{
    Layout = null;
}

<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>
</head>
<body>
   <div></div>
</body>
</html>

В последующих разделах мы рассмотрим различные аспекты представления Razor и продемонстрируем разнообразные вещи, которые с ним можно делать. При изучении Razor полезно помнить, что представления существуют для выражения пользователю одной или более частей модели - и это означает генерацию HTML-разметки, которая отображает данные, извлеченные из одного или множества объектов. Если не забывать, что мы всегда пытаемся строить HTML-страницу, которая может быть отправлена клиенту, то вся активность механизма визуализации Razor обретает смысл.

Работа с объектом модели

Давайте начнем с самой первой строки в представлении:

@model Razor.Models.Product

Операторы Razor начинаются с символа "@". В этом случае оператор @model объявляет тип объекта модели, который будет передаваться представлению из метода действия Это позволяет ссылаться на методы, поля и свойства объекта модели представления с помощью @Model, как показано в примере ниже, где демонстрируется простое дополнение к представлению Index.cshtml:

@model Razor.Models.Product

@{
    Layout = null;
}

<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>
</head>
<body>
   <div>
       @Model.Name
   </div>
</body>
</html>

Обратите внимание, что тип объекта модели представления объявляется с использованием @model (со строчной буквой "m"), а доступ к свойству Name производится с применением @Model (с прописной буквой "M"). Поначалу это может немного запутывать, но со временем станет вполне привычным.

Запустив проект, вы увидите вывод, показанный на рисунке:

Результат чтения значения свойства внутри представления

С помощью выражения @model мы сообщаем инфраструктуре MVC вид объекта, с которым будет выполняться работа, а среда Visual Studio использует эту информацию парой способов. Прежде всего, при написании кода представления при вводе @Model с последующей точкой среда Visual Studio откроет окно с предполагаемыми именами членов:

Среда Visual Studio предлагает предположительные варианты имен членов, основываясь на выражении @Model

Не менее полезно и то, что Visual Studio будет помечать ошибки при наличии проблем с членами объекта модели представления, на которые производится ссылка. Пример этого можно видеть на рисунке ниже, где предпринимается попытка ссылки на несуществующее свойство. Среде Visual Studio известно, что класс Product, указанный в качестве типа модели, не содержит такого свойства, поэтому ошибка подсвечивается в редакторе.

Среда Visual Studio сообщает о проблеме с выражением @Model

Работа с компоновками

Ниже приведено еще одно выражение Razor из файла представления Index.cshtml:

@{
    Layout = null;
}

Это пример блока кода Razor, который позволяет включать в представление операторы C#. Блок кода открывается посредством "@{" и закрывается с помощью "}", а содержащиеся в нем операторы оцениваются при визуализации представления.

Показанный выше блок кода устанавливает значение свойства Layout в null. Как будет объясняться позже, представления Razor компилируются в классы C# приложения MVC и в базовом классе, который они используют, определено свойство Layout. Достаточно знать, что результатом установки свойства Layout в null является уведомление инфраструктуры MVC о том, что наше представление является самодостаточным, и оно будет визуализировать все свое содержимое, которое необходимо возвратить клиенту.

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

Создание компоновки

Чтобы создать компоновку, щелкните правой кнопкой мыши на папке Views в окне Solution Explorer, выберите в контекстном меню пункт Add --> New Item и укажите шаблон MVC 5 Layout Page (Razor), как показано на рисунке ниже:

Создание новой компоновки

Введите _BasicLayout.cshtml (обратите внимание, что первым символом является подчеркивание) в поле Name и щелкните на кнопке Add для создания файла. Сгенерированное Visual Studio для этого файла содержимое приведено в примере ниже:

<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>@ViewBag.Title</title>
</head>
<body>
    <div>
        @RenderBody()
    </div>
</body>
</html>

Файлы в папке Views, имена которых начинаются с символа подчеркивания "_", не возвращаются пользователю. С помощью таких имен файлов можно различать представления, которые должны визуализироваться, и файлы, предназначенные для их поддержки. Имена файлов компоновок, являющиеся файлами поддержки, снабжаются префиксом в виде подчеркивания.

Компоновки - это специализированная форма представлений. Вызов метода @RenderBody() вставляет в разметку компоновки содержимое представления, указанное методом действия. Другое выражение Razor в компоновке обращается к свойству по имени Title в объекте ViewBag, чтобы установить содержимое элемента title.

Элементы компоновки будут применяться к любому представлению, которое использует эту компоновку, и именно поэтому компоновки, в сущности, являются шаблонами. Чтобы продемонстрировать, как это работает, в примере ниже к компоновке добавлена простая разметка:

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>@ViewBag.Title</title>
</head>
<body>
    <h1>Информация о товаре</h1>
    <div style="padding: 20px; border: solid medium black; font-size: 20pt">
        @RenderBody()
    </div>
    <h2>Посетите сайт <a href="http://professorweb.ru">ProfessorWeb</a></h2>
</body>
</html>

Здесь была добавлена пара элементов заголовка и применены стили CSS к элементу div, содержащему выражение @RenderBody. Это сделано просто для прояснения, какое содержимое поступает из компоновки, а какое - из представления.

Применение компоновки

Чтобы применить компоновку к представлению, нужно всего лишь установить значение свойства Layout. Компоновка содержит HTML-элементы, которые определяют структуру ответа для браузера, поэтому их можно удалить из представления. В примере ниже демонстрируется применение компоновки, что существенно упрощает содержимое файла Index.cshtml:

@model Razor.Models.Product

@{
    Layout = "~/Views/_BasicLayout.cshtml";
    ViewBag.Title = "Название товара";
}

Название товара: @Model.Name

Здесь также устанавливается значение свойства ViewBag.Title, которое будет использоваться в качестве содержимого для элемента title в HTML-документе, отправляемом обратно пользователю - это необязательная, однако хорошая практика. Если значение этого свойства не задано, инфраструктура MVC возвратит пустой элемент title.

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

Результат применения простой компоновки к представлению

Использование файла запуска представления

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

Решить указанную проблему можно с использованием файла запуска представления. При визуализации представления инфраструктура MVC ищет файл с именем _ViewStart.cshtml. Содержимое этого файла будет трактоваться так, если бы оно присутствовало внутри самого файла представления, и это средство можно применять для автоматической установки значения свойства Layout. Чтобы создать файл запуска представления, добавьте в папку Views новый файл представления и назовите его _ViewStart.cshtml (опять-таки, обратите внимание на подчеркивание в начале имени) и приведите его содержимое в соответствие с кодом:

@{
    Layout = "~/Views/_BasicLayout.cshtml";
}

Файл запуска представления содержит значение для свойства Layout, а это означает что соответствующий оператор из файла Index.cshtml можно удалить:

@model Razor.Models.Product

@{
    ViewBag.Title = "Название товара";
}

Название товара: @Model.Name

Специально указывать, что должен применяться файл запуска представления, не требуется. Инфраструктура MVC автоматически обнаруживает данный файл и использует его содержимое. Преимущество отдается значениям, определяемым в файле представления, и это позволяет легко переопределять файл запуска представления.

Важно понимать разницу между удалением свойства Layout из файла представления и его установкой в null. Если представление является самодостаточным и применять компоновку не нужно, установите свойство Layout в null. Если просто не указывать свойство Layout, инфраструктура MVC предполагает, что компоновка необходима, и должно использоваться значение, найденное в файле запуска представления _ViewStart.cshtml.

Демонстрация разделяемых компоновок

В качестве быстрой и простой демонстрации совместного использования компоновок к контроллеру Home добавлен новый метод действия по имени NameAndPrice. Определение этого метода приведено в примере ниже, содержащем измененное содержимое файла HomeController.cs:

using System.Web.Mvc;
using Razor.Models;

namespace Razor.Controllers {

    public class HomeController : Controller {
        Product myProduct = new Product {
            ProductID = 1,
            Name = "Каяк",
            Description = "Лодка на одного человека",
            Category = "Водные виды спорта",
            Price = 275M
        };

        public ActionResult Index() {
            return View(myProduct);
        }

        public ActionResult NameAndPrice() {
            return View(myProduct);
        }
    }
}

Этот метод действия передает объект myProduct методу View(), в точности как это делает метод действия Index(); подобное вряд ли встретится в реальном проекте, но для демонстрации функциональности Razor вполне подойдет и такой очень простой пример. Щелкните правой кнопкой мыши на методе действия NameAndPrice и выберите в контекстном меню пункт Add View (Добавить представление), чтобы открыть диалоговое окно Add View. Заполните это диалоговое окно, как показано на рисунке ниже: введите в поле View Name имя NameAndPrice, выберите в списке Template вариант Empty, а в списке Model Class (Класс модели) - вариант Product:

Создание представления, которое использует компоновку

Удостоверьтесь, что флажок Use a layout page (Использовать страницу компоновки) отмечен и обратите внимание на текст ниже поля ввода. Он гласит, что вы должны оставить это поле пустым, если используемое представление было указано в файле запуска представления. Если в данный момент щелкнуть на кнопке Add, представление будет создано без оператора C#, устанавливающего значение свойства Layout.

Мы собираемся явно указать представление, поэтому щелкните на кнопке с троеточием справа от текстового поля. Среда Visual Studio отобразит диалоговое окно, которое позволяет выбрать файл компоновки:

Выбор файла компоновки

Соглашение, принятое для проекта MVC, предполагает помещение файлов компоновки в папку Views. Тем не менее, это всего лишь соглашение, поэтому левая панель окна позволяет осуществлять навигацию в рамках проекта на тот случай, если вы решили не придерживаться указанного соглашения.

Мы определили только один файл компоновки, поэтому выберите файл _BasicLayout.cshtml и щелкните на кнопке ОК для возврата в диалоговое окно Add View. Вы увидите, что имя файла компоновки появилось в текстовом поле:

Указание файла компоновки при создании представления

Щелкните на кнопке Add для создания файла /Views/Home/NameAndPrice.cshtml Содержимое этого файла приведено в примере ниже:

@model Razor.Models.Product

@{
    ViewBag.Title = "NameAndPrice";
    Layout = "~/Views/_BasicLayout.cshtml";
}

В случае указания компоновки среда Visual Studio применяет слегка отличающееся стандартное содержимое для файлов представлений, но внутри них находятся те же самые выражения Razor, которые мы используем, когда применяем компоновку к представлению напрямую.

В завершение этого примера ниже показано простое добавление к файлу NameAndPrice.cshtml, которое отображает значения данных из объекта модели представления:

@model Razor.Models.Product

@{
    ViewBag.Title = "Название и цена";
    Layout = "~/Views/_BasicLayout.cshtml";
}

<h2>Название и цена</h2>
Название продукта - @Model.Name, он стоит $@Model.Price

Как и ожидалось, общие элементы и стили, определенные в компоновке, будут применены и к представлению, что демонстрирует возможность использования компоновки для создания общего внешнего вида (несмотря на его простоту и непривлекательность в рассматриваемом примере).

Мы получили бы тот же самый результат, если бы оставили текстовое поле в диалоговом окне Add View пустым и понадеялись на файл запуска представления. Файл компоновки был указан явно только для того, чтобы продемонстрировать средство Visual Studio, помогающее делать выбор.

Пройди тесты
Лучший чат для C# программистов