Создание HTML-форм
149ASP.NET --- ASP.NET MVC 5 --- Создание HTML-форм
Инфраструктура ASP.NET MVC Framework включает набор встроенных вспомогательных методов, которые помогают управлять созданием HTML-элементов <form>. В этой статье будет показано, как применять такие вспомогательные методы. Мы продолжим рассматривать пример проекта HelperMethods, который создали в предыдущей статье.
Создание элементов формы
Одним из наиболее часто используемых видов взаимодействия в веб-приложении является HTML-форма, и для ее поддержки предназначено несколько разных вспомогательных методов. Для демонстрации работы вспомогательных методов, связанных с формами, в пример проекта внесен ряд дополнений. Первым делом в папке Models был создан новый файл класса по имени User.cs. Содержимое этого файла приведено в примере ниже:
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
}
}
Тип User будет служить классом модели представления во время исследования вспомогательных методов, связанных с формами, а типы Address и Role помогут продемонстрировать несколько более сложных функций.
Кроме того, в контроллер Home были добавлены новые методы действий для работы с этими объектами моделей:
using System.Web.Mvc;
using HelperMethods.Models;
namespace HelperMethods.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
// ...
}
public ActionResult CreateUser()
{
return View(new User());
}
[HttpPost]
public ActionResult CreateUser(User User)
{
return View(User);
}
}
}
Это стандартный подход с двумя методами к работе с HTML-формами, при котором мы полагаемся на привязку моделей ASP.NET MVC Framework в создании объекта User из данных формы и передаче его методу действия с атрибутом HttpPost.
Мы никак не обрабатываем данные из формы, поскольку хотим сосредоточиться на том, как генерировать элементы в представлении. Метод действия с атрибутом HttpPost просто вызывает метод View() и передает ему объект User, получаемый в качестве параметра, что в результате приводит к отображению данных формы пользователю.
Мы собираемся начать со стандартной построенной вручную HTML-формы и продемонстрировать замену различных ее частей с применением вспомогательных методов. Начальная версия формы показана в примере ниже; она находится в файле представления CreateUser.cshtml внутри папки /Views/Home:
@model HelperMethods.Models.User
@{
ViewBag.Title = "CreateUser";
}
<h2>Создать пользователя</h2>
<form action="/Home/CreateUser" method="post">
<div class="item">
<label>UserId</label>
<input name="userId" value="@Model.UserId" />
</div>
<div class="item">
<label>Имя</label>
<input name="FirstName" value="@Model.FirstName" />
</div>
<div class="item">
<label>Фамилия</label>
<input name="LastName" value="@Model.LastName" />
</div>
<input type="submit" value="Отправить" />
</form>
Это представление содержит стандартную вручную созданную форму, в которой значения атрибута value элементов <input> устанавливаются с использованием объекта модели.
Обратите внимание, что атрибуты name всех элементов <input> установлены в соответствие со свойствами модели, которые эти элементы отображают. Атрибут name применяется стандартным связывателем моделей ASP.NET MVC Framework для выяснения того, какие элементы <input> содержат значения для свойств типа модели, при обработке запроса POST. Если опустить атрибут name, форма не будет корректно функционировать.
Далее создается папка Views/Shared, в которую добавляется файл компоновки по имени 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; }
</style>
</head>
<body>
@RenderBody()
</body>
</html>
Это простая компоновка с несколькими стилями CSS, предназначенными для элементов <input> формы. Запустив приложение и перейдя на URL вида /Home/CreateUser, можно наблюдать базовую функциональность формы:

Из-за того, что данные формы никак не используются в приложении, щелчок на кнопке "Отправить" приводит к повторному отображению этих данных. Ниже приведена HTML-разметка, которую пример приложения MVC отправляет браузеру - мы будем использовать ее для демонстрации изменений, вызванных вспомогательными методами:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CreateUser</title>
<style>
label { display: inline-block; width: 100px; }
.item { margin: 5px; }
</style>
</head>
<body>
<h2>Создать пользователя</h2>
<form action="/Home/CreateUser" method="post">
<div class="item">
<label>UserId</label>
<input name="userId" value="0" />
</div>
<div class="item">
<label>Имя</label>
<input name="FirstName" />
</div>
<div class="item">
<label>Фамилия</label>
<input name="LastName" />
</div>
<input type="submit" value="Отправить" />
</form>
</body>
</html>
Применение вспомогательных методов для генерации HTML-элементов вроде <form> и <input> является необязательным. При желании их можно закодировать с помощью статических HTML-дескрипторов и заполнить значения с использованием данных представления или объектов модели представления, как это делалось в настоящем разделе.
HTML-разметка, которая генерируется из вспомогательных методов в последующих разделах, является довольно ясной и в ней отсутствуют какие-то специальные значения атрибутов или трюки. Это позволяет легко обеспечить синхронизацию HTML-разметки с приложением, так что, например, изменения в конфигурации маршрутизации будут автоматически отражены в формах. Вспомогательные методы здесь используются для удобства, а не из-за того, что они создают важную или специальную HTML-разметку, поэтому вы не обязаны их применять, если они не согласуются с принятым стилем разработки.
Создание элементов form
Двумя наиболее полезными (и часто используемыми) вспомогательными методами являются BeginForm() и EndForm(). Они создают HTML-дескрипторы <form> и генерируют для формы допустимый атрибут action, который основан на механизме маршрутизации в приложении.
Существуют 13 разных версий метода BeginForm(), которые позволяют последовательно указывать характеристики результирующего элемента <form>. В рассматриваемом примере приложения нам нужна только самая базовая версия, не принимающая аргументов и создающая элемент <form>, атрибут action которого обеспечивает отправку формы тому же самому методу действия, что привел к генерации текущего представления.
В примере ниже показано, как применяется эта перегруженная версия BeginForm() и вспомогательный метод EndForm(). Вспомогательный метод EndForm() имеет только одно определение и просто закрывает элемент <form>, добавляя в представление дескриптор </form>:
@model HelperMethods.Models.User
@{
ViewBag.Title = "CreateUser";
}
<h2>Создать пользователя</h2>
@{Html.BeginForm();}
<div class="item">
<label>UserId</label>
<input name="userId" value="@Model.UserId" />
</div>
<div class="item">
<label>Имя</label>
<input name="FirstName" value="@Model.FirstName" />
</div>
<div class="item">
<label>Фамилия</label>
<input name="LastName" value="@Model.LastName" />
</div>
<input type="submit" value="Отправить" />
@{Html.EndForm();}
Обратите внимание, что вызовы этих вспомогательных методов должны трактоваться как операторы C#. Причина в том, что указанные вспомогательные методы записывают свои дескрипторы напрямую в вывод. Хотя проектное решение выглядит довольно неуклюжим, это не имеет особого значения, поскольку вспомогательные методы BeginForm() и EndForm() редко применяются подобным образом. Намного более распространенный подход, продемонстрированный в примере ниже, предусматривает помещение вызова вспомогательного метода BeginForm() внутрь выражения using. В конце блока using исполняющая среда .NET вызовет метод Dispose() объекта, возвращенного методом BeginForm(), который, в свою очередь, вызовет метод EndForm():
@model HelperMethods.Models.User
@{
ViewBag.Title = "CreateUser";
}
<h2>Создать пользователя</h2>
@using (Html.BeginForm()) {
...
}
Такой подход называется самозакрывающейся формой и ему следует отдавать предпочтение. Удобно, когда блок кода содержит форму и дает возможность легко понять, какие элементы будут находиться между открывающим и закрывающим дескрипторами <form>.
Остальные 12 вариаций метода BeginForm() позволяют изменять различные аспекты создаваемого элемента <form>. Среди этих перегруженных версий встречается много повторений, т.к. они позволяют последовательно указывать детали формы. В таблице ниже перечислены наиболее важные перегруженные версии, которые регулярно используются в приложениях MVC. Остальные не показанные здесь перегруженные версии метода BeginForm() предназначены для совместимости с версиями инфраструктуры ASP.NET MVC Framework, которые выходили до появления в языке C# поддержки создания динамических объектов.
Перегруженная версия | Описание |
---|---|
BeginForm() | Создает форму, осуществляющую обратную отправку методу действия, который привел к визуализации представления |
BeginForm(action, controller) | Создает форму, которая осуществляет обратную отправку методу действия и контроллеру, указанным в виде строк |
BeginForm(action, controller, method) | Подобна предыдущей перегруженной версии, но позволяет указывать значение для атрибута method, используя перечисление System.Web.Mvc.FormMethod (POST, GET и т.п.) |
BeginForm(action, controller, method, attributes) | Подобна предыдущей перегруженной версии, но позволяет указывать в качестве атрибутов для элемента <form> объект, свойства которого используются в качестве имен атрибутов |
BeginForm(action, controller, routeValues, method, attributes) | Подобна предыдущей перегруженной версии, но позволяет указывать значения для переменных сегментов маршрута в конфигурации маршрутизации приложения. Значения задаются в виде объекта, свойства которого соответствуют переменным сегментов маршрута |
Ранее демонстрировалась простейшая версия метода BeginForm(), которой было вполне достаточно для примера приложения, а в примере ниже можно видеть, как используется наиболее сложная версия, позволяющая указывать дополнительную информацию относительно конструирования элемента <form>:
...
@using (Html.BeginForm("CreateUser", "Home",
new { id = "MyIdValue" },
FormMethod.Post,
new { @class = "userCssClass", data_formType="user" }))
{
...
}
В этом примере явно указывается набор деталей, которые иначе были бы выведены инфраструктурой ASP.NET MVC Framework автоматически, такие как имя действия и контроллер. Мы также указали, что форма должна быть отправлена с применением HTTP-метода POST, который использовался бы в любом случае.
Более интересными аргументами являются те, которые устанавливают значения для переменной маршрута и атрибуты для элемента <form>. Аргументы значений маршрутов применяются для указания значения переменной сегмента id в стандартном маршруте, добавленном Visual Studio в файл /App_Start/RouteConfig.cs при создании проекта. Кроме того, определяются атрибут class и атрибут данных data_formType. (Атрибуты данных - это специальные атрибуты, которые можно добавлять к элементам для выполнения обработки HTML-содержимого.)
Ниже показан HTML-дескриптор <form>, который порождается этим вызовом BeginForm():
... <form action="/Home/CreateUser/MyIdValue" class="userCssClass" data-formType="user" method="post"> ...
Как видите, значение для атрибута id было добавлено к целевому URL, а к элементу были применены атрибут class и атрибут данных. Обратите внимание, что в вызове метода BeginForm() указывался атрибут по имени data_formType, но в выводе он превратился в data-formType. Дело в том, что указывать имена свойств динамического объекта C#, содержащие символы дефиса, не допускается, поэтому использовался символ подчеркивания, который в выводе был автоматически заменен символом дефиса, аккуратно устраняя несоответствие между синтаксисами C# и HTML. (И, разумеется, имя свойства class было снабжено префиксом @, что позволило применить зарезервированное ключевое слово C# в качестве имени свойства для атрибута class.)
Указание маршрута, используемого формой
Когда используется метод BeginForm(), инфраструктура ASP.NET MVC Framework ищет в конфигурации маршрутизации первый маршрут, подходящий для применения при генерации URL, который будет нацелен на требуемое действие и контроллер. В сущности, выбор маршрута оставлен за вами. Если вы хотите обеспечить использование конкретного маршрута, можете применять вместо BeginForm() вспомогательный метод BeginRouteForm(). Чтобы продемонстрировать эту возможность, в файл /App_Start/RouteConfig.cs добавлен новый маршрут, как показано в примере ниже:
using System.Web.Mvc;
using System.Web.Routing;
namespace HelperMethods
{
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
routes.MapRoute(
name: "FormRoute",
url: "app/forms/{controller}/{action}"
);
}
}
}
Если вызвать метод BeginForm() с такой конфигурацией маршрутизации, получится элемент <form> с атрибутом action, содержащим URL, который создан из стандартного маршрута. В примере ниже видно, как с помощью метода BeginRouteForm() указать на необходимость использования этого нового маршрута:
...
@using (Html.BeginRouteForm("FormRoute", null, FormMethod.Post,
new { @class = "userCssClass", data_formType="user" }))
{
...
}
В результате генерируется следующий дескриптор <form>, атрибут action которого соответствует структуре нового маршрута:
... <form action="/app/forms/Home/CreateUser" class="userCssClass" data-formType="user" method="post"> ...
Подобно BeginForm(), метод BeginRouteForm() имеет множество перегруженных версий, которые позволяют указывать разнообразные детали для элемента <form>. Все они следуют той же самой структуре, как их аналоги BeginForm().