ASP.NET MVC 4

ASP.NET MVC 4

Адам Фриман

Использование выражений Razor

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

В хорошем MVC приложении существует четкое разделение между ролями, которые выполняют метод действия и представление. Для этой главы правила просты, и мы свели их в таблице 5-1.

Таблица 5-1: Роли, выполняемые методом действия и представлением
Компонент Выполняет Не выполняет
Action Method Передает объект модели представления в представление Не передает отформатированные данные в представление
View Использует объект модели представления для показа контента пользователю Не меняет ни один из аспектов объекта модели представления

Мы будем возвращаться к этой теме снова и снова на протяжении всей книги. Чтобы получить все лучшее от MVC фреймворка, вам нужно уважать разделение различных частей приложения и соблюдать его. Вы увидите, что многое можно сделать с Razor, в том числе с использованием операторов C#, но вы ни в коем случае не должны использовать Razor для выполнения операций с бизнес логикой или для манипулирования объектами доменной модели.

Равным образом, вы не должны форматировать данные, которые ваш метод действия передает представлению. Вместо этого, позвольте представлению вычислить данные, которые оно должно отобразить. Вы видели очень простой пример этого в предыдущих разделах данной главы. Мы определили метод действия NameAndPrice, который отображает значения свойств Name и Price объекта Product. И хотя мы знали, какие именно свойства нам нужно отобразить, мы передали весь объект Product модели представления, вот так:

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

Затем в представлении мы использовали Razor выражение @Model, чтобы получить значения интересующих нас свойств:

The product name is @Model.Name and it costs $@Model.Price

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

Вставка значений данных

Самое простое, что вы можете сделать с Razor выражением, это вставить значение данных в разметку. Вы можете сделать это, используя выражение @Model, чтобы обратиться к свойствам и методам, определенным в модели представления. Или же вы можете использовать выражение @ViewBag, чтобы обратиться к свойствам, которые вы определили динамически при помощи контейнера ViewBag (мы представили ее в главе 2).

Вы уже видели примеры обоих этих выражений, но для полноты картины мы добавили новый метод действия DemoExpressions в контроллер Home, который передает данные в представление, используя объект модели и ViewBag. Вы можете ознакомиться с определением нового метода действия в листинге 5-13.

Листинг 15-13: Метод действия DemoExpression
using Razor.Models;
using System;
using System.Web.Mvc;
namespace Razor.Controllers
{
	public class HomeController : Controller
	{
		Product myProduct = new Product
		{
			ProductID = 1,
			Name = "Kayak",
			Description = "A boat for one person",
			Category = "Watersports",
			Price = 275M
		};
		public ActionResult Index()
		{
			return View(myProduct);
		}
		public ActionResult NameAndPrice()
		{
			return View(myProduct);
		}
		public ActionResult DemoExpression()
		{
			ViewBag.ProductCount = 1;
			ViewBag.ExpressShip = true;
			ViewBag.ApplyDiscount = false;
			ViewBag.Supplier = null;
			return View(myProduct);
		}
	}
}

Мы создали строго типизированное представление DemoExpression.cshtml, чтобы показать эти основные типы выражения данных. Вы можете увидеть содержимое файла представления в листинге 5-14.

Листинг 5-14: Содержание файла представления DemoExpression
@model Razor.Models.Product
@{
	ViewBag.Title = "DemoExpression";
}
<table>
	<thead>
		<tr>
			<th>Property</th>
			<th>Value</th>
		</tr>
	</thead>
	<tbody>
		<tr>
			<td>Name</td>
			<td>@Model.Name</td>
		</tr>
		<tr>
			<td>Price</td>
			<td>@Model.Price</td>
		</tr>
		<tr>
			<td>Stock Level</td>
			<td>@ViewBag.ProductCount</td>
		</tr>
	</tbody>
</table>

Для этого примера мы создали простую HTML таблицу и использовали свойства объекта модели и ViewBag для заполнения ячеек значениями. На рисунке 5-12 вы можете увидеть результат запуска приложений и переход по адресу /Home/DemoExpression. Это всего лишь еще одна демонстрация основных Razor выражений, которые мы до сих пор использовали в примерах.

Рисунок 5-12: Использование основных выражений Razor для вставки значений данных в HTML разметку

Результат не особо красивый, потому что мы не применяли никаких CSS стилей к HTML элементам, которые генерируют представление и макет. Однако этот пример служит для четкого понимания того, каким образом могут быть использованы основные Razor выражения для отображения данных, передаваемых от метода действия к представлению.

Установка значений атрибутов

Все наши примеры до сих пор касались содержания элементов, но вы также можете использовать Razor выражения для установки значений атрибутов элементов. В листинге 5-15 показано, как мы изменили представление DemoExpression, чтобы использовать ViewBag свойства для значений атрибутов. Способ, которым Razor обрабатывает атрибуты в MVC 4, довольно разумный и интересный, и это одна из тех областей, которые были улучшены после MVC 3.

Листинг 5-15: Использование Razor выражения для установки значения атрибута
@model Razor.Models.Product
@{
	ViewBag.Title = "DemoExpression";
	Layout = "~/Views/_BasicLayout.cshtml";
}
<table>
	<thead>
		<tr>
			<th>Property</th>
			<th>Value</th>
		</tr>
	</thead>
	<tbody>
		<tr>
			<td>Name</td>
			<td>@Model.Name</td>
		</tr>
		<tr>
			<td>Price</td>
			<td>@Model.Price</td>
		</tr>
		<tr>
			<td>Stock Level</td>
			<td>@ViewBag.ProductCount</td>
		</tr>
	</tbody>
</table>
<div data-discount="@ViewBag.ApplyDiscount" data-express="@ViewBag.ExpressShip"
	data-supplier="@ViewBag.Supplier">
	The containing element has data attributes
</div>
Discount:<input type="checkbox" checked="@ViewBag.ApplyDiscount" />
Express:<input type="checkbox" checked="@ViewBag.ExpressShip" />
Supplier:<input type="checkbox" checked="@ViewBag.Supplier" />

Мы начали с использования основных Razor выражений для установки значений некоторых атрибутов data элемента div. data-атрибуты, которые являются атрибутами, чьи имена начинаются с data-, были неформальным способом создания пользовательских атрибутов в течение многих лет и стали стандартом в HTML5. Мы использовали значения свойств ViewBag ApplyDiscount, ExpressShip и Supplier, чтобы установить значения этих атрибутов.

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

<div data-discount="False" data-express="True" data-supplier="">
	The containing element has data attributes
</div>

Значения False и True соответствуют булевым ViewBag значениям, но Razor сделал кое-что разумное для свойства, чье значение равно null, а именно, представил пустую строку.

Но все становится гораздо более интересным, когда мы посмотрим на второе наше дополнение в представлении: это набор чекбоксов, чей атрибут checked установлен на те же самые ViewBag свойства, которые мы использовали для атрибутов data. HTML, который представил Razor, выглядит следующим образом:

Discount: <input type="checkbox" />
Express: <input type="checkbox" checked="checked" />
Supplier: <input type="checkbox" />

В MVC 4 Razor стал понимать, как используется такие атрибуты как checked, где наличие атрибута, а не его значение, меняет конфигурацию элемента. Если бы Razor вставили False, или null, или пустую строку в качестве значения атрибута checked, тогда в чекбоксе, который отображает браузер, стояла бы галочка. Вместо этого Razor полностью удаляет атрибут из элемента, если значение равно false или null, создавая нужный результат, как показано на рисунке 5-13.

Рисунок 5-13: Результат удаления атрибутов, чье присутствие влияет на элемент

Использование условных операторов

Razor способен обрабатывать условные операторы, что обозначает, что мы можем подгонять выходные данные наших представлений, основываясь на значениях данных в нашем представлении. Мы собираемся добраться до самого сердца Razor, что позволяет нам создавать сложные и гибкие макетов, которые остаются простыми для понимания и поддержки. В листинге 5-16 мы обновили наш файл представления DemoExpression.cshtml, чтобы включить в него условный оператор.

Листинг 5-16: Использование условного оператора Razor
@model Razor.Models.Product
@{
	ViewBag.Title = "DemoExpression";
	Layout = "~/Views/_BasicLayout.cshtml";
}
<table>
	<thead>
		<tr>
			<th>Property</th>
			<th>Value</th>
		</tr>
	</thead>
	<tbody>
		<tr>
			<td>Name</td>
			<td>@Model.Name</td>
		</tr>
		<tr>
			<td>Price</td>
			<td>@Model.Price</td>
		</tr>
		<tr>
			<td>Stock Level</td>
			<td>@switch ((int)ViewBag.ProductCount) {
					case 0:
						@: Out of Stock
						break;
					case 1:
						<b>Low Stock (@ViewBag.ProductCount)</b>
						break;
					default:
						@ViewBag.ProductCount
						break;
					}
			</td>
		</tr>
	</tbody>
</table>

Чтобы написать условный оператор, поместите символ @ в начале этого оператора, в данном примере перед ключевым словом C# switch. Блок кода заканчивается фигурной скобкой (}) так же, как и обычный блок кода C#.

Совет

Обратите внимание, что мы привели значение свойства ViewBag.ProductCount к int, чтобы использовать его с оператором switch. Это необходимо, поскольку оператор switch работает только с определенным набором типов, и он не может оценить динамическое свойство без приведения типов.

Внутри блока кода Razor вы можете вставлять HTML элементы и значения данных для представления, просто определив HTML и Razor выражения вот таким образом:

...
<b>Low Stock (@ViewBag.ProductCount)</b>
...
@ViewBag.ProductCount
...

Нам не нужно заключать элементы или выражения в кавычки или как-то по-особенному обозначать их: движок Razor будет интерпретировать их как выходные данные, которые будут обрабатываться в обычном порядке. Однако если вы хотите вставить в представление просто текст, когда он не содержится в HTML элементе, вы должны помочь Razor и обозначить строку следующим образом:

...
@: Out of Stock
...

Символ @: предотвращения Razor от интерпретации строки как C# выражения, что является поведением по умолчанию, когда он сталкивается с текстом. Вы можете увидеть результат использования нашего условного оператора на рисунке 5-14.

Рисунок 5-14: Использование условного оператора switch в представлении Razor

Условные операторы играют важную роль в Razor представлениях, потому что они позволяют подгонять содержание к значениям данных, которые представление получает от метода действия. В качестве дополнительного примера в листинге 5-17 показано добавление оператора if к представлению DemoExpression.cshtml – вы, естественно, знаете, что это очень широко используемый условный оператор.

Листинг 5-17: Использование условного оператора if в представлении Razor
@model Razor.Models.Product
@{
ViewBag.Title = "DemoExpression";
Layout = "~/Views/_BasicLayout.cshtml";
}
<table>
	<thead>
		<tr>
			<th>Property</th>
			<th>Value</th>
		</tr>
	</thead>
	<tbody>
		<tr>
			<td>Name</td>
			<td>@Model.Name</td>
		</tr>
		<tr>
			<td>Price</td>
			<td>@Model.Price</td>
		</tr>
		<tr>
			<td>Stock Level</td>
			<td>@if (ViewBag.ProductCount == 0) {
							@:Out of Stock
					} else if (ViewBag.ProductCount == 1) {
							<b>Low Stock (@ViewBag.ProductCount)</b>
					} else {
							@ViewBag.ProductCount
					}
			</td>
		</tr>
	</tbody>
</table>

Этот условный оператор дает тот же результат, что и switch, но мы просто хотели показать, как вы можете связывать условные операторы C# с Razor представлениями. Мы объясняем, как это все работает, в главе 18, когда мы более углубленно рассмотрим представления.

Перечисление содержимого массивов и коллекций

При написании MVC приложений вам часто будет нужно перечислять содержимое массива или другого вида коллекции объектов, и генерировать подробный контент каждого из них. Чтобы показать, как это делается, мы определили новый метод действия DemoArray в контроллере Home, который вы можете увидеть в листинге 5-18.

Листинг 5-18: Метод действия DemoArray
using Razor.Models;
using System;
using System.Collections.Generic;
using System.Web.Mvc;
namespace Razor.Controllers
{
	public class HomeController : Controller
	{
		Product myProduct = new Product
		{
			ProductID = 1,
			Name = "Kayak",
			Description = "A boat for one person",
			Category = "Watersports",
			Price = 275M
		};
		// ...другие методы действия опущены для краткости...
		public ActionResult DemoArray()
		{
			Product[] array = {
				new Product {Name = "Kayak", Price = 275M},
				new Product {Name = "Lifejacket", Price = 48.95M},
				new Product {Name = "Soccer ball", Price = 19.50M},
				new Product {Name = "Corner flag", Price = 34.95M}
			};
			return View(array);
		}
	}
}

Этот метод действия создает объект Product[], который содержит несколько простых значений данных, и передает его методу View, и эти данные показываются при помощи представления по умолчанию.

Visual Studio не предлагает опции для массивов и коллекций при создании представления, поэтому вам придется вручную ввести информацию о требуемом типе в диалоговом окне Add View. Вы можете увидеть, как мы сделали это, на рисунке 5-15. Тут показано, как мы создали представление DemoArray.cshtml и указали, что тип модели представления – это Razor.Models.Product[].

Рисунок 5-15: Установка вручную типа модели представления для строго типизированного представления

Вы можете увидеть содержимое файла представления DemoArray.cshtml в листинге 5-19, куда включены дополнения, которые мы сделали, чтобы показать информацию по элементам массива пользователи.

Листинг 5-19: Содержимое файла DemoArray.cshtml
@model Razor.Models.Product[]
@{
	ViewBag.Title = "DemoArray";
	Layout = "~/Views/_BasicLayout.cshtml";
}
@if (Model.Length > 0) {
<table>
	<thead>
		<tr>
			<th>Product</th>
			<th>Price</th>
		</tr>
	</thead>
	<tbody>
		@foreach (Razor.Models.Product p in Model) {
		<tr>
			<td>@p.Name</td>
			<td>$@p.Price</td>
		</tr>
		}
	</tbody>
</table>
} else {
	<h2>No product data</h2>
}

Мы использовали оператор @if, чтобы менять содержание в зависимости от длины массива, с которым мы работаем, и оператор @foreach, чтобы перечислить содержимое массива и создать строку в HTML таблицу для каждого из элементов. Вы видите, как эти выражения соответствуют своим C# копиям, и как мы создали локальную переменную р в цикле foreach, а затем обратились к ее свойствам, используя Razor выражения @p.Name и @p.Price.

В результате мы получаем элемент h2, если массив пуст, или, в противном случае, создаем одну строку для каждого элемента массива в HTML таблице. Поскольку в этом примере наши данные являются статическими, вы всегда будете видеть один и тот же результат, который представлен на рисунке 5-16.

Рисунок 5-16: Генерирование элементов при помощи оператора foreach

Работа с пространством имен

Вы заметили, что в последнем примере мы должны были обращаться к классу Product в цикле foreach по его полному имени, вот таки образом:

...
@foreach (Razor.Models.Product p in Model) {
...

Это может раздражать в сложных представлениях, где у вас будет много ссылок на модели представления и другие классы. Мы можем привести в порядок представления при помощи выражения @using, чтобы вставить пространство имен в контекст, как и для обычного C# класса. В листинге 5-20 показано, как мы применили выражение @using для представления DemoArray.cshtml, которое мы создали ранее.

Листинг 5-20: Применение выражения @using
@using Razor.Models
@model Product[]
@{
	ViewBag.Title = "DemoArray";
	Layout = "~/Views/_BasicLayout.cshtml";
}
@if (Model.Length > 0) {
<table>
	<thead>
		<tr>
			<th>Product</th>
			<th>Price</th>
		</tr>
	</thead>
	<tbody>
		@foreach (Product p in Model) {
		<tr>
			<td>@p.Name</td>
			<td>$@p.Price</td>
		</tr>
		}
	</tbody>
</table>
} else {
	<h2>No product data</h2>
}

Представление может содержать несколько выражений @using. Мы использовали выражение @using, чтобы импортировать пространство имен Razor.Models, что обозначает, что мы можем удалить пространство имен из выражения @model и из цикла foreach.

или RSS канал: Что новенького на smarly.net