Главная страница   /   20.4. Настройка системы шаблонных вспомогательных методов (ASP.NET MVC 4

ASP.NET MVC 4

ASP.NET MVC 4

Адам Фриман

20.4. Настройка системы шаблонных вспомогательных методов

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

Создаем пользовательский шаблон Editor

Самый простой способ изменить поведение шаблонного вспомогательного метода – создать пользовательский шаблон. Это позволит нам сгенерировать для свойства модели именно такой HTML, какой мы хотим.

Чтобы продемонстрировать, как работает этот подход, мы создадим пользовательский шаблон для свойства Role класса Person. Тип этого свойства - значение из перечисления Role, и нам не нравится то, как оно визуализируется. Шаблонный вспомогательный метод создает обычный элемент input, который позволяет пользователю ввести любое значение, не ограничивая их теми, которые указаны в перечислении.

MVC Framework будет искать пользовательские шаблоны для элементов editor в папке /Views/Shared/EditorTemplates, поэтому мы добавили эту папку в проект и создали в ней новое строго типизированное частичное представление под названием Role.cshtml. Вы можете увидеть содержимое этого файла в листинге 20-19.

Листинг 20-19: Содержимое файла Role.cshtml
@model HelperMethods.Models.Role

@Html.DropDownListFor(m => m,
	new SelectList(Enum.GetNames(Model.GetType()),
		Model.ToString()))

Тип модели для данного представления – это перечисление Role, и мы используем вспомогательный метод Html.DropDownListFor, чтобы создать элемент select с элементами option для значений в перечислении. Мы передаем дополнительное значение в конструктор SelectList; оно указывает выбранное значение, которое мы получаем от объект модели представления. Метод DropDownListFor и объект SelectList работают со строковыми значениями, поэтому мы должны преобразовать значения в перечислении и модели представления.

Когда мы будем использовать шаблонный вспомогательный метод, чтобы создать элемент editor для типа Role, будет использоваться файл /Views/Shared/EditorTemplates/Role.cshtml. Таким образом, мы получаем последовательное и полезное представление для типа данных. Эффект применения пользовательского шаблона показан на рисунке 20-11.

Рисунок 20-11: Эффект применения пользовательского шаблона для перечисления Role

Порядок поиска шаблонов

Шаблон Role.cshtml работает потому, что платформа MVC сначала выполняет поиск пользовательских шаблонов для данного типа C#, и только потом использует один из встроенных шаблонов. Поиск подходящего шаблона MVC Framework проводит в следующем порядке:

  1. Шаблон, переданный во вспомогательный метод - например, вспомогательный метод Html.EditorFor(m => m.SomeProperty, "MyTemplate") нашел бы шаблон MyTemplate.
  2. Шаблон, который указан в атрибутах метаданных, таких как UIHint.
  3. Шаблон, который связан с типом данных, указанным в метаданных, например в атрибуте DataType.
  4. Шаблон, который соответствует имени класса .NET обрабатываемого типа данных.
  5. Встроенный шаблон String, если обрабатываемый тип данных является простым.
  6. Шаблон, который соответствует базовому классу типа данных.
  7. Если тип данных реализует IEnumerable, то будет использоваться встроенный шаблон Collection. Если ничего не подходит, то будет использоваться шаблон Object, на который распространяется правило, что формирование шаблонов не является рекурсивным.

В некоторых пунктах используются встроенные шаблоны, которые описаны в таблице 20-4. На каждом этапе процесса поиска шаблона MVC Framework ищет шаблон под названием EditorTemplates/<name> для вспомогательных методов для элементов editor и DisplayTemplates/<name> для вспомогательных методов для элементов display. Для нашего шаблона Role поиск остановится на пункте 4; мы создали шаблон под названием Role.cshtml и поместили его в папку /Views/Shared/EditorTemplates.

Поиск пользовательских шаблонов проводится по той же схеме, что и поиск обычных представлений, а следовательно, мы можем создать пользовательский шаблон для конкретного контроллера и поместить его в папку ~/Views/<controller>/EditorTemplates, чтобы переопределить шаблоны из папки ~/Views/Shared. Подробная информация о поиске представлений дана в главе 18.

Создаем общий (generic) шаблон

Можно создавать шаблоны не только для конкретного типа. Мы можем, например, создать шаблон для всех перечислений, а затем выбрать его с помощью атрибута UIHint. Если вы посмотрите блок «Порядок поиска шаблонов», то увидите, что шаблоны, заданные с помощью атрибута UIHint, имеют приоритет над шаблонами для конкретного типа.

Чтобы продемонстрировать, как это работает, мы создали новое представление под названием Enum.cshtml в папке /Views/Shared/EditorTemplates. Содержимое этого файла показано в листинге 20-20.

Листинг 20-20: Представление Enum.cshtml
@model Enum
@Html.DropDownListFor(m => m, Enum.GetValues(Model.GetType())
	.Cast<enum>()
	.Select(m =>
	{
		string enumVal = Enum.GetName(Model.GetType(), m);
		return new SelectListItem()
		{
			Selected = (Model.ToString() == enumVal),
			Text = enumVal,
			Value = enumVal
		};
	})

Типом модели для этого шаблона является Enum, что позволяет нам работать с любым перечислением. Для разнообразия мы создали с помощью LINQ строки, которые необходимы для создания элементов select и option. Далее мы можем затем применить атрибут UIHint. В нашем проекте есть дополняющий класс метаданных, так что мы применили этот атрибут к классу PersonMetadata, как показано в листинге 20-21. (Напомним, что этот класс находится по адресу /Models/Metadata/PersonMetadata.cs).

Листинг 20-21: Используем атрибут UIHint, чтобы указать пользовательский шаблон
using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;

namespace HelperMethods.Models
{
	[DisplayName("New Person")]
	public partial class PersonMetaData
	{
		[HiddenInput(DisplayValue = false)]
		public int PersonId { get; set; }

		[Display(Name = "First")]
		public string FirstName { get; set; }

		[Display(Name = "Last")]
		public string LastName { get; set; }

		[Display(Name = "Birth Date")]
		[DataType(DataType.Date)]
		public DateTime BirthDate { get; set; }

		[Display(Name = "Approved")]
		public bool IsApproved { get; set; }

		[UIHint("Enum")]
		public Role Role { get; set; }
	}
}

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

Заменяем встроенные шаблоны

Если мы создадим пользовательский шаблон с таким же именем, как и один из встроенных шаблонов, MVC Framework будет использовать пользовательскую версию в предпочтение встроенной. В листинге 20-22 показано содержимое файла Boolean.cshtml, который мы создали в папке /Views/Shared/EditorTemplates. Это представление заменяет встроенный шаблон Boolean, который используется для отображения значений bool и bool?.

Листинг 20-22: Заменяем встроенный шаблон
@model bool?

@if (ViewData.ModelMetadata.IsNullableValueType && Model == null) {
	@:(True) (False) <b>(Not Set)</b>
} else if (Model.Value) {
	@:<b>(True)</b> (False) (Not Set)
} else {
	@:(True) <b>(False)</b> (Not Set)
}

В данном представлении мы отображаем все возможные значения и выделяем то, которое соответствует объекту модели. Результат применения этого шаблона показан на рисунке 20-12.

Рисунок 20-12: Результат переопределения встроенного шаблона для элементов editor

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