Шаблоны отображения и редактирования — очень мощная и полезная фича ASP.Net MVC, которую я активно использую и рекомендую всем без исключения.
С помощью шаблонов можно легко и быстро отображать и редактировать модели данных, что весьма способствует быстрому прототипированию, а также помогает в случаях, когда внешний вид форм на сайте у вас более-менее стандартизован.
Предположим, что в контроллере у вас есть типичная модель регистрации:
public class RegistrationModel { public string Login { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public DateTime Birthdate { get; set; } ... }
Что вам нужно сделать, чтобы отобразить эту форму на сайте?
Обычно, что-то вроде:
@using (Html.BeginForm()) { <div> Login <br />@Html.TextBoxFor(x => x.Login)<br /> Firstname <br />@Html.TextBoxFor(x => x.FirstName)<br /> Lastname <br />@Html.TextBoxFor(x => x.LastName)<br /> Birthdate <br />@Html.TextBoxFor(x => x.Birthdate)<br /> <input type="submit" /> </div> }
При этом получим мы что-то вроде:
С помощью шаблонов редактирования, код вьюшки можно свести к одной строчке:
@using (Html.BeginForm()) { @Html.EditorForModel() <input type="submit" /> }
Результат в браузере при этом будет фактически такой же.
То же верно и для отображения данных формы (без возможности редактирования). Имея во вьюшке всего одну строчку:
@Html.DisplayForModel()
У вас в модели есть поле ID, которое отображать не нужно, а при редактировании оно должно быть Hidden? Нет ничего проще!
public class RegistrationModel { [HiddenInput(DisplayValue = false)] public int ID { get; set; } public string Login { get; set; } public string FirstName { get; set; } public string LastName { get; set; } ... }
При использовании шаблонов отображения/редактирования MVC проходится по всем полям вашей модели и последовательно выводит имена полей и соответствующие значения или input’ы для редактирования.
Если в некоторых случаях для определенных полей нет необходимости отображать label’ы, то можно сделать так:
public class RegistrationModel { [HiddenInput(DisplayValue = false), UIHint("")] public int ID { get; set; } public string Login { get; set; } }
Если это выглядит слегка неочевидно, то можно объявить свой атрибут:
public class RegistrationModel { [DisplayLabel(false)] public string Login { get; set; } } [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)] public class DisplayLabelAttribute : Attribute, IMetadataAware { private readonly bool _visible; public DisplayLabelAttribute(bool visible) { _visible = visible; } public void OnMetadataCreated(ModelMetadata metadata) { metadata.HideSurroundingHtml = !_visible; } }
Огромный плюс шаблонов в том, что в них настраивается практически всё. Не нравится отображение полей формы «в столбик», а хочется табличного вида вроде этого:
Просто переопределите шаблон отображения для типа Object! Для этого нужно создать папку DisplayTemplates (или EditorTemplates для шаблонов редактирования) в папке ~/Views/Shared (или ~/Views/ControllerName/Shared) и создайте там файл Object.cshtml:
@if (ViewData.TemplateInfo.TemplateDepth > 5) { if (Model == null) { @ViewData.ModelMetadata.NullDisplayText } else { @ViewData.ModelMetadata.SimpleDisplayText } } else { <table cellpadding="0" cellspacing="0" border="0"> @foreach (var prop in ViewData.ModelMetadata.Properties.Where(pm => pm.ShowForDisplay && !ViewData.TemplateInfo.Visited(pm))) { if (prop.HideSurroundingHtml) { @Html.Editor(prop.PropertyName) } else { <tr> <td> <div class="display-label" style="text-align: right;"> @prop.GetDisplayName() </div> </td> <td width="10"> </td> <td> <div class="display-field"> @Html.Editor(prop.PropertyName) </div> </td> </tr> } } </table> }
Аналогично можно переопределить шаблоны и для любого другого типа, то есть использовав на стадии прототипирования @Html.EditorForModel() для отображения RegistrationModel мы можем в дальнейшем просто создать файл RegistrationModel.cshtml в папке EditorTemplates (строготипизировав его от типа RegistrationModel)- и получить кастомный шаблон редактирования/отображения, который будет содержать нужный нам GUI.
Использовать шаблоны также очень удобно в случае сложных вложенных форм:
public class OrderTicketsModel { public RegistrationModel FirstPassenger { get; set; } public RegistrationModel SecondPassenger { get; set; } }
Такую, казалось бы сложную, модель шаблонизатор тоже достаточно неплохо отрисовывает:
Наконец, как еще одно из применений шаблонизаторов, можно выделить создание собственных визуальных контролов для отображения стандартных элементов.
Например, посмотрим, как отображается по умолчанию enum:
public enum Sex { Male, Female } public class Model2 { public Sex Sex { get; set; } } public ActionResult Index2() { return View(new Model2()); }
Вводить текст в данном случае кажется совсем не удобным. Логично сделать выбор через RadioButton или DropDownList, правда?
Пример шаблона Enum_AsDropDownList.cshtml:
@model Enum @{ // Looks for a [Display(Name="Some Name")] or a [Display(Name="Some Name", ResourceType=typeof(ResourceFile)] Attribute on your enum var listItems = Enum.GetValues(Model.GetType()).OfType<Enum>().Select(e => new SelectListItem() { Text = e.GetDescription(), Value = e.ToString(), Selected = e.Equals(Model) }); string prefix = ViewData.TemplateInfo.HtmlFieldPrefix; ViewData.TemplateInfo.HtmlFieldPrefix = string.Empty; @Html.DropDownList(prefix, listItems) }
Применить шаблон к нашей модельке очень просто:
public class Model2 { [UIHint("Enum_AsDropDownList")] public Sex Sex { get; set; } }
И выглядеть это будет как-то так:
Пример для радио баттонов, а также все предыдущие примеры можно посмотреть «в живую» на небольшом тестовом проекте.
Все полученные формы достаточно легко стилизовать при помощи css.
Итак, в каких же случаях это действительно удобно?
- Прототипирование. Когда вас не слишком заботит внешний вид приложения, а нужно продемонстрировать функционал — использование шаблонов позволит сэкономить время и легко преобразовать «тестовый проект» в реальное приложение в дальнейшем — потребуется только добавление кастомных файлов шаблона
- Значительное количество однотипных по формату отображения форм. В этом случае затраты на однократное переопределение шаблона для типа Object вполне окупается
- Создание визуальных «контролов» для типизированного отображения некоторых объектов доменной области — в данном случае удобство продиктовано «коробочностью» решения. Аналогом может служить создание html-хэлперов для отображения таких объектов, но придется каждый раз вспоминать имя функции, которое отобразило бы объект :) В случае шаблонов стандартный подход: @Html.EditorFor(x => x.FirstPassenger) универсален
В каких же случаях использовать шаблоны не стоит? В основном, это те случаи, когда отображения нешаблонное и повторное его использование не подразумевается: форма отображается только на одном экране, или в зависимости от экранов она выглядит очень по-разному. В этом случае внесение дополнительной сложности в виде разделения экрана на два файла (вьюшка и display/editor шаблон для формы) не имеет большого смысла.
Однако, хотя использование шаблонов для форм целиком в этом случае и не оправданно, вполне можно использовать в рамках таких форм шаблоны для отдельных элементов моделей (например, для отображения enum’а, как было показано выше).
Эта заметка — лишь краткое вступление в функционал шаблонов редактирования/отображения. Если вам понравилась идея и хочется узнать больше о тонкостях реализации, увидеть шаблоны по-умолчанию для различных типов, встроенные в MVC, посмотреть на другие варианты применения — добро пожаловать в первоисточник или его русифицированный вариант.
Тестовый проект со всем упомянутым выше: DisplayEditorTemplatesForBlog.zip
Унылое говно эти твои шаблоны :)
Кажется, ты еще забыл сказать, что аннотации типа «[UIHint]», «[HiddenInput]» и прочее тоже отстой :)
Я подразумевал :)