Шаблоны отображения и редактирования форм в ASP.Net MVC (DisplayTemplates/EditorTemplates)

Шаблоны отображения и редактирования — очень мощная и полезная фича 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

Опубликовать в Facebook
Опубликовать в Google Plus

3 комментария

  1. Кажется, ты еще забыл сказать, что аннотации типа «[UIHint]», «[HiddenInput]» и прочее тоже отстой :)

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *