Шаблонизированные вспомогательные методы

111

Показанные в предыдущей статье вспомогательные методы HTML, такие как Html.CheckBoxFor() и Html.TextBoxFor(), генерируют элементы специфического типа. Это значит, что вы должны решить заранее, какие виды элементов должны использоваться для представления свойств модели, и вручную обновлять представления, если тип свойства изменился.

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

Пример проекта

Мы будем продолжать работу с проектом HelperMethod, ранее. В нем был создан класс модели User вместе с парой поддерживающих типов. В качестве напоминания все это показано в примере ниже:

using System;

namespace HelperMethods.Models
{
    public class User
    {
        public int UserId { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public DateTime BirthDate { get; set; }
        public Address HomeAddress { get; set; }
        public bool IsApproved { get; set; }
        public Role Role { get; set; }
    }

    public class Address
    {
        public string Line1 { get; set; }
        public string Line2 { get; set; }
        public string City { get; set; }
        public string PostalCode { get; set; }
        public string Country { get; set; }
    }

    public enum Role
    {
        Admin,
        User,
        Guest
    }
}

Пример проекта содержит очень простой контроллер Home, который применяется для отображения форм и получения отправок этих форм. Определение класса HomeController приведено в примере ниже:

using System.Web.Mvc;
using HelperMethods.Models;

namespace HelperMethods.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            ViewBag.Fruits = new string[] { "Яблоко", "Апельсин", "Груша" };
            ViewBag.Cities = new string[] { "Москва", "Лондон", "Париж" };

            string message = "Это HTML-элемент: <input>";

            return View((object)message);
        }

        public ActionResult CreateUser()
        {
            return View(new User());
        }

        [HttpPost]
        public ActionResult CreateUser(User User)
        {
            return View(User);
        }
    }
}

В этой статье будут использоваться два метода действий CreateUser(), которые визуализируют представление из файла /Views/Home/CreateUser.cshtml. В примере ниже показано представление CreateUser, построенное в конце предыдущей статьи, с небольшим изменением:

@model HelperMethods.Models.User

@{
    ViewBag.Title = "CreateUser";
    Html.EnableClientValidation(false);
}

<h2>Создать пользователя</h2>
@using (Html.BeginRouteForm("FormRoute", null, FormMethod.Post,
    new { @class = "userCssClass", data_formType="user" }))
{ 
    <div class="item">
        <label>UserId</label>
        @Html.TextBoxFor(user => user.UserId)
    </div>
    <div class="item">
        <label>Имя</label>
        @Html.TextBoxFor(user => user.FirstName)
    </div>
    <div class="item">
        <label>Фамилия</label>
        @Html.TextBoxFor(user => user.LastName)
    </div>
    <div class="item">
        <label>Роль</label>
        @Html.DropDownListFor(user => user.Role,
            new SelectList(Enum.GetNames(typeof(HelperMethods.Models.Role))))
    </div>
    <input type="submit" value="Отправить" />
}

В разметку, показанную в примере, внесено одно дополнение, которое выделено полужирным. По умолчанию вспомогательные методы будут добавлять к генерируемым HTML-элементам атрибуты данных, предназначенные для поддержки варианта проверки достоверности форм, который демонстрировался во время создания приложения GameStore. В этой статье такие атрибуты не нужны, поэтому с помощью метода Html.EnableClientValidation() они отключаются для представления CreateUser. Средство проверки достоверности на стороне клиента по-прежнему включено для остальной части приложения.

Использование шаблонизированных вспомогательных методов

Первыми мы изучим шаблонизированные вспомогательные методы Html.Editor() и Html.EditorFor(). Метод Editor() принимает строковый аргумент, указывающий свойство, для которого требуется элемент редактора. Вспомогательный метод следует процессу поиска, для нахождения соответствующего свойства в объекте ViewBag и в объекте модели. Метод EditorFor() является строго типизированным эквивалентом, позволяющим использовать лямбда-выражение для указания свойства модели, для которого необходим элемент редактора.

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

@model HelperMethods.Models.User

@{
    ViewBag.Title = "CreateUser";
    Html.EnableClientValidation(false);
}

<h2>Создать пользователя</h2>
@using (Html.BeginRouteForm("FormRoute", null, FormMethod.Post,
    new { @class = "userCssClass", data_formType="user" }))
{ 
    <div class="item">
        <label>UserId</label>
        @Html.Editor("UserId")
    </div>
    <div class="item">
        <label>Имя</label>
        @Html.Editor("FirstName")
    </div>
    <div class="item">
        <label>Фамилия</label>
        @Html.EditorFor(user => user.LastName)
    </div>
    <div class="item">
        <label>Роль</label>
        @Html.EditorFor(user => user.Role)
    </div>
    <div class="item">
        <label>Дата рождения</label>
        @Html.EditorFor(user => user.BirthDate)
    </div>
    <input type="submit" value="Отправить" />
}

Методы Editor() и EditorFor() создают одни и те же HTML-элементы. Разница связана только со способом указания свойства, для которого создается элемент редактора. Результаты внесенных изменений можно просмотреть, запустив пример приложения и перейдя на URL вида /Home/CreateUser:

Использование вспомогательных методов Editor() и EditorFor() внутри формы

Кроме добавленного свойства BirthDate форма не выглядит отличающейся от той, что создавалась ранее. Однако была сгенерирована другая HTML-разметка, соответствующая стандартам HTML5. В спецификации HTML5 определены различные типы элементов <input>, которые могут применяться для редактирования общих типов данных, таких как числа и даты. Методы Editor() и EditorFor() используют тип редактируемого свойства для выбора одного из новых типов - это можно видеть в примере ниже, в котором приведена HTML-разметка, сгенерированная для формы:

<form action="/app/forms/Home/CreateUser" class="userCssClass" 
      data-formType="user" method="post">    
    <div class="item">
        <label>UserId</label>
        <input ... type="number" />
    </div>
    <div class="item">
        <label>Имя</label>
        <input ... type="text" />
    </div>
    <div class="item">
        <label>Фамилия</label>
        <input ... type="text" />
    </div>
    <div class="item">
        <label>Роль</label>
        <input ... type="text" />
    </div>
    <div class="item">
        <label>Дата рождения</label>
        <input ... type="datetime" />
    </div>
    <input type="submit" value="Отправить" />
</form>

В атрибуте type указывается разновидность элемента <input>, который должен быть отображен в браузере. Вспомогательные методы задают типы number и datetime для свойств userId и BirthDate и тип text, используемый по умолчанию для остальных свойств.

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

Далее будет показано, как предоставить инфраструктуре ASP.NET MVC Framework дополнительную информацию, которая улучшит HTML-разметку, генерируемую вспомогательными методами, но перед тем, как погружаться в детали, необходимо взглянуть на другие доступные шаблонизированные вспомогательные методы. Полный список вспомогательных методов приведен в таблице ниже, и все они будут продемонстрированы в последующих разделах.

Шаблонизированные вспомогательные методы HTML в ASP.NET MVC Framework
Вспомогательный метод Описание
Display()

Визуализирует представление, допускающее только чтение, для указанного свойства модели, выбирая HTML-элемент в соответствие с типом и метаданными свойства

DisplayFor()

Строго типизированная версия предыдущего вспомогательного метода

Editor()

Визуализирует редактор для указанного свойства модели, выбирая HTML-элемент в соответствие с типом и метаданными свойства

EditorFor()

Строго типизированная версия предыдущего вспомогательного метода

Label()

Визуализирует HTML-элемент label, ссылающийся на указанное свойство модели

LabelFor()

Строго типизированная версия предыдущего вспомогательного метода

Генерация меток и элементов для отображения

Для демонстрации работы других вспомогательных методов в пример приложения добавляется возможность отображения представления, допускающего только чтение, с данными, которые отправлены вместе с HTML-формой. Для начала модифицируем версию HttpPost действия CreateUser в контроллере Home, как показано в примере ниже:

using System.Web.Mvc;
using HelperMethods.Models;

namespace HelperMethods.Controllers
{
    public class HomeController : Controller
    {
        // ...

        [HttpPost]
        public ActionResult CreateUser(User User)
        {
            return View("DisplayUser", User);
        }
    }
}

Затем в папку /Views/Home добавляется файл представления DisplayUser.cshtml, с содержимым, показанным в примере ниже:

@model HelperMethods.Models.User
@{
    ViewBag.Title = "DisplayUser";
}

<h2>Данные пользователя</h2>
<div class="item">
    @Html.LabelFor(user => user.UserId)
    @Html.Display("UserId")
</div>
<div class="item">
    @Html.Label("Имя")
    @Html.Display("FirstName")
</div>
<div class="item">
    @Html.Label("Фамилия")
    @Html.DisplayFor(m => m.LastName)
</div>
<div class="item">
    @Html.Label("Роль")
    @Html.DisplayFor(m => m.Role)
</div>
<div class="item">
    @Html.Label("Дата рождения")
    @Html.DisplayFor(m => m.BirthDate)
</div>

Чтобы посмотреть на вывод, который производит это представление, необходимо запустить приложение, перейти на URL вида /Home/CreateUser, заполнить форму и щелкнуть на кнопке "Отправить". Результат показан на рисунке ниже; несложно заметить, что мы сделали небольшой шаг назад, поскольку вспомогательные методы Label() и LabelFor() в качестве содержимого для меток использовали имена свойств:

Применение вспомогательных методов для генерации представления с элементами только для чтения

Ниже показана HTML-разметка, созданная этим примером. Здесь можно видеть, что вспомогательные методы Display() и DisplayFor() по умолчанию не генерируют HTML-элемент. Они просто выдают значение свойства, на котором оперируют.

<h2>Данные пользователя</h2>
<div class="item">
    <label for="UserId">UserId</label>
    13
</div>
<div class="item">
    <label for="">Имя</label>
    Alex
</div>
<div class="item">
    <label for="">Фамилия</label>
    Erohin
</div>
<div class="item">
    <label for="">Роль</label>
    Admin
</div>
<div class="item">
    <label for="">Дата рождения</label>
    01.01.0001 0:00:00
</div>

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

Использование шаблонизированных вспомогательных методов для целой модели

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

Шаблонизированные вспомогательные методы в ASP.NET MVC Framework
Вспомогательный метод Описание
DisplayForModel()

Визуализирует представление, предназначенное только для чтения, для целого объекта модели

EditorForModel()

Визуализирует элементы редактора для целого объекта модели

LabelForModel()

Визуализирует HTML-элемент label, ссылающийся на целый объект модели

Это не тот же самый вид формирования шаблонов, который разработчики из Microsoft добавили в Visual Studio для создания компонентов MVC, подобных контроллерам и представлениям, но базовая идея аналогична: вывод генерируется на основе характеристик типа данных. В случае Visual Studio выводом при формировании шаблонов является файл класса или кода Razor, а в случае шаблонизированных вспомогательных методом вывод представляет собой HTML-разметку.

В примере ниже показано, как посредством вспомогательных методов LabelForModel() и EditorForModel() упростить представление CreateUser.cshtml:

@model HelperMethods.Models.User

@{
    ViewBag.Title = "CreateUser";
    Html.EnableClientValidation(false);
}

<h2>Создать пользователя: @Html.LabelForModel()</h2>
@using (Html.BeginRouteForm("FormRoute", null, FormMethod.Post,
    new { @class = "userCssClass", data_formType="user" }))
{ 
    @Html.EditorForModel();
    <input type="submit" value="Отправить" />
}

Результат работы вспомогательных методов для формирования шаблонов можно видеть на рисунке ниже:

Использование вспомогательных методов для формирования шаблонов при создании редактора для объекта модели User

Здесь легко заметить, что именно делают эти вспомогательные методы, однако пока еще все далеко от идеала. Вспомогательный метод LabelForModel() не сгенерировал полезную метку. Несмотря на то что отображается больше свойств объекта модели User, чем было определено вручную в предыдущих примерах, видны не все свойства (например, Address), а те, что видны, не всегда удобны в использовании (в частности, свойство Role лучше было бы выразить в виде элемента select, а не input).

Частично проблема заключается в том, что HTML-разметка, генерируемая вспомогательными методами для формирования шаблонов, не соответствует CSS-стилям, которые были добавлены в файл /Views/Shared/_Layout.cshtml ранее. Ниже приведен пример HTML-разметки, сгенерированной для редактирования свойства FirstName:

...
<div class="editor-label">
    <label for="FirstName">FirstName</label>
</div>
<div class="editor-field">
    <input class="text-box single-line" id="FirstName" 
        name="FirstName" type="text" value="" />
</div>
...

Внешний вид представления можно привести в порядок, включив в компоновку стили, которые соответствуют значениям в CSS-атрибутах class, добавленным к элементам <div> и <input> вспомогательными методами для формирования шаблонов. В примере ниже показаны изменения, внесенные в файл _Layout.cshtml:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>@ViewBag.Title</title>
    <style>
        label {
            display: inline-block;
            width: 100px;
        }
        .item {
            margin: 5px;
        }
        h2 > label {
            width: inherit;
        }
        .editor-label, .editor-field {
            float: left;
            margin-top: 10px;
        }
       .editor-field input {
                height: 20px;
        }
        .editor-label {
            clear: left;
        }
        .editor-field {
            margin-left: 10px;
        }
        input[type=submit] {
            float: left;
            clear: both;
            margin-top: 10px;
        }
        .column {
            float: left;
            margin: 10px;
        }
    </style>
</head>
<body>
    @RenderBody()
</body>
</html>

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

Эффект от стилизации элементов с использованием классов, определенных вспомогательными методами для формирования шаблонов

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

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