5. Вигляд та його шаблон
5.1. Introduction
Повернемося до архітектури додатка ASP.NET MVC:
![]() |
У попередньому розділі ми розглянули, як ASP.NET MVC представляв інформацію запиту [1] дії [2a] у вигляді шаблону, який міг містити обмеження валідації. Ця модель подавалася як вхідні дані для дії, і ми назвали її моделлю дії. Тепер ми розглянемо найпоширеніший результат дії — тип [ViewResult], який відповідає представленню V [3] разом із його моделлю M [2c]. Цю модель ми назвемо моделлю подання V; її не слід плутати з моделлю дії, яку ми щойно розглядали. Одна є вхідними даними дії, інша — вихідними.
Почнемо зі створення нового проєкту [Exemple-03] [1], як і раніше, у межах того самого рішення, типу базового ASP.NET MVC:
![]() |
Створимо контролер із назвою [First] [2]. Код, згенерований для цього контролера, виглядає так:
using System.Web.Mvc;
namespace Exemple_03.Controllers
{
public class FirstController : Controller
{
public ActionResult Index()
{
return View();
}
}
}
- рядки 7–10: створено дію [Index]. Тип результату методу [Index] відповідає класу [ActionResult], від якого походять більшість можливих результатів дії;
- рядок 9: метод [View] класу [Controller] (рядок 5) повертає тип [ViewResult], який походить від [ActionResult]. Цей метод допускає численні перевантаження. Ми розглянемо деякі з них. Основна з них така:
![]() |
- перший параметр — це ім’я подання. Якщо його немає, використовується подання з тим самим ім’ям, що й дія, яка генерує [ViewResult], і яке шукатиметься у папці [/Views/{controller}], де {controller} — це ім’я контролера;
- другий — це шаблон подання. Якщо його немає, подання не має шаблону.
Метод [Index], наведений нижче:
public ActionResult Index()
{
return View();
}
запитує у подання [/Views/First/Index.cshtml] відобразити себе. Він не передає йому жодного шаблону. Створімо [1] у папці [/Views/First]:
![]() |
потім створимо в ньому подання [Index] [2]:
![]() |
![]() |
Вкажіть ім’я подання як [3]. Воно створюється як [4]. Згенерований код такий:
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
</head>
<body>
<div>
</div>
</body>
</html>
Це класичний HTML, за винятком рядків 1–3, які є кодом C#. Програма, що керує поданнями, називається двигуном подань. Він відповідає за обробку всього, що не є HTML, перетворюючи це на HTML. Зрештою, саме це буде надіслано клієнту. Двигун подання тут називається [Razor]. Він дозволяє вбудовувати код C# у представлення. [Razor] інтерпретує цей код C# і на його основі генерує код HTML. Ось кілька основних правил щодо вбудовування коду C# у представлення:
- перехід з HTML на C# відбувається при зустрічі з символом @ (рядок 1). Якщо цей символ позначає блок коду, слід поставити фігурні дужки (рядки 1 і 3). Якщо він позначає змінну, значення якої потрібно отримати, просто напишіть @variable;
- перехід з C# на HTML відбувається при виявленні символу < (рядок 5). Іноді доводиться примусово ініціювати це переключення, зокрема коли на сторінку вставляється текст у вигляді простого тексту без тегів HTML. У такому разі для введення тексту використовується тег <text>: <text>тут текст у вигляді простого тексту</text>.
Рядок 2 вище вказує, що вид [Index] не має шаблону сторінки.
Змінимо вигляд наступним чином:
@{
Layout = null;
string vue = "Index";
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
</head>
<body>
<div>
<h3>Vue @vue</h3>
</div>
</body>
</html>
- рядок 3: визначає змінну C#;
- рядок 15: виводить значення цієї змінної.
Тепер запустимо URL [/First/Index]:
![]() |
Отриманий код HTML має такий вигляд:
Це чистий документ HTML. Усі коди C# зникли.
5.2. Використання [ViewBag] для передачі інформації до подання
Ми створюємо нову дію під назвою [Action01], пов’язану з поданням [Action01.cshtml]:
![]() |
Дія [Action01] виглядає так:
// Дія01
public ViewResult Action01()
{
ViewBag.info = string.Format("Contrôleur={0}, Action={1}", RouteData.Values["controller"], RouteData.Values["action"]);
return View();
}
- рядок 4: використовується властивість [ViewBag] контролера. Це динамічний об’єкт, до якого можна додавати властивості, як це зроблено в рядку 4. Особливістю цього об’єкта є те, що він також доступний для подання. Отже, це спосіб передачі йому інформації;
- рядок 5: запитується вид за замовчуванням для дії. Це вид [/First/Action01.cshtml]. Йому не передається жодна модель.
Вигляд [Action01.cshtml] має такий вигляд:
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Action01</title>
</head>
<body>
<div>
<h4>@ViewBag.info</h4>
</div>
</body>
</html>
- рядок 14: відображається властивість [ViewBag.info].
Давайте перевіримо. Запитуємо URL [/First/Action01]:
![]() |
5.3. Використання строго типізованого шаблону для передачі інформації до подання
Недоліком попереднього методу є те, що він не дозволяє виявляти помилки до виконання. Отже, якщо представлення [Action01.cshtml] використовує код
<h4>@ViewBag.Info</h4>
, виникне помилка, оскільки властивість [Info] не існує. Властивість, створена дією [Action01], називається [info]. Щоб уникнути цієї проблеми, можна використати строго типізовану модель.
В одному з розглянутих раніше прикладів дія була такою:
// Дія10
public ContentResult Action10(ActionModel03 modèle)
{
string erreurs = getErrorMessagesFor(ModelState);
string texte = string.Format("email={0}, jour={1}, info1={2}, info2={3}, info3={4}, erreurs={5}",
modèle.Email, modèle.Jour, modèle.Info1, modèle.Info2, modèle.Info3, erreurs);
return Content(texte, "text/plain", Encoding.UTF8);
}
Дія [Action10] передавала своєму клієнту шість елементів інформації (Email, День, Інформація1, Інформація2, Інформація3, помилки) у вигляді символьного рядка. Ми передаватимемо ці дані у моделі подання [ViewModel01]. Оскільки ця модель використовує дані з [ActionModel03], ми зробимо її похідною від цього класу.
Спочатку скопіюємо [ActionModel03] із проєкту [Exemple-02] у поточний проєкт [Exemple-03]:
![]() |
і змінюємо його простір імен, щоб він відповідав простору імен проекту [Exemple-03]:
using System.ComponentModel.DataAnnotations;
namespace Exemple_03.Models
{
public class ActionModel03
{
[Required(ErrorMessage = "Le paramètre email est requis")]
[EmailAddress(ErrorMessage = "Le paramètre email n'a pas un format valide")]
public string Email { get; set; }
[Required(ErrorMessage = "Le paramètre jour est requis")]
[RegularExpression(@"^\d{1,2}$", ErrorMessage = "Le paramètre jour doit avoir 1 ou 2 chiffres")]
public string Jour { get; set; }
[Required(ErrorMessage = "Le paramètre info1 est requis")]
[MaxLength(4, ErrorMessage = "Le paramètre info1 ne peut avoir plus de 4 caractères")]
public string Info1 { get; set; }
[Required(ErrorMessage = "Le paramètre info2 est requis")]
[MinLength(2, ErrorMessage = "Le paramètre info2 ne peut avoir moins de 2 caractères")]
public string Info2 { get; set; }
[Required(ErrorMessage = "Le paramètre info3 est requis")]
[MinLength(4, ErrorMessage = "Le paramètre info3 doit avoir 4 caractères exactement")]
[MaxLength(4, ErrorMessage = "Le paramètre info3 doit avoir 4 caractères exactement")]
public string Info3 { get; set; }
}
}
- рядок 2: новий простір імен;
Потім створюємо клас [ViewModel01]:
![]() |
Код класу [ViewModel01] виглядає так:
namespace Exemple_03.Models
{
public class ViewModel01 : ActionModel03
{
public string Erreurs { get; set; }
}
}
- рядок 3: клас успадковує [ActionModel03] і, отже, властивості [Email, Jour, Info1, Info2, Info3];
- рядок 5: до нього додається властивість [Erreurs].
Тепер ми пишемо дію [Action02], яка:
- приймає як вхідні дані модель дії [ActionModel03];
- та видає на вихід модель подання [ViewModel01].
Її код такий:
// Дія 02
public ViewResult Action02(ActionModel03 modèle)
{
string erreurs = getErrorMessagesFor(ModelState);
return View(new ViewModel01(){Email=modèle.Email, Jour=modèle.Jour, Info1=modèle.Info1, Info2=modèle.Info2, Info3=modèle.Info3, Erreurs=erreurs});
}
- рядок 1: [Action02] отримує модель дії [ActionModel03]. Вона повертає результат типу [ViewResult];
- рядок 4: помилки, пов’язані з шаблоном дії [ActionModel03], об’єднуються в рядок [erreurs]. Метод [getErrorMessagesFor] було описано на сторінці 65 та включено до контролера [First] нового проєкту;
- рядок 5: метод [View] викликається з одним параметром. Це модель подання. Подання не вказано. Тому буде використано подання за замовчуванням [/Views/First/Action02]. Модель подання [ViewModel01] інстанціюється та ініціалізується за допомогою п’яти даних з моделі дії [ActionModel03] та даних [erreurs], сформованих у рядку 4.
Тепер ми створюємо представлення [/First/Action02.cshtml]:
![]() |
Його код такий:
@model Exemple_03.Models.ViewModel01
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Action02</title>
</head>
<body>
<h3>Informations du modèle de vue</h3>
<ul>
<li>Email : @Model.Email</li>
<li>Jour : @Model.Jour</li>
<li>Info1 : @Model.Info1</li>
<li>Info2 : @Model.Info2</li>
<li>Info3 : @Model.Info3</li>
<li>Erreurs : @Model.Erreurs</li>
</ul>
</body>
</html>
- Нововведення міститься у рядку 1. Позначення [@model] визначає тип шаблону подання. На цей шаблон потім посилається позначення [@Model] (рядки 16–21);
- рядки 15–22: інформація про шаблон відображається у списку.
Розглянемо кілька прикладів виконання дії [Action02].
Спочатку без параметрів:
![]() |
потім із неправильними параметрами:
![]() |
потім із правильними параметрами:
![]() |
У цьому прикладі шаблон подання [ViewModel01] переймає інформацію з шаблону дії [ActionModel03]. Таке трапляється часто. У такому разі можна використовувати єдиний шаблон, який слугуватиме одночасно шаблоном дії та подання. Створюємо нову модель [ActionModel04]:
![]() |
який матиме такий вигляд:
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
namespace Exemple_03.Models
{
[Bind(Exclude="Erreurs")]
public class ActionModel04
{
// ---------------------- Дія --------------------------------
[Required(ErrorMessage = "Le paramètre email est requis")]
[EmailAddress(ErrorMessage = "Le paramètre email n'a pas un format valide")]
public string Email { get; set; }
[Required(ErrorMessage = "Le paramètre jour est requis")]
[RegularExpression(@"^\d{1,2}$", ErrorMessage = "Le paramètre jour doit avoir 1 ou 2 chiffres")]
public string Jour { get; set; }
[Required(ErrorMessage = "Le paramètre info1 est requis")]
[MaxLength(4, ErrorMessage = "Le paramètre info1 ne peut avoir plus de 4 caractères")]
public string Info1 { get; set; }
[Required(ErrorMessage = "Le paramètre info2 est requis")]
[MinLength(2, ErrorMessage = "Le paramètre info2 ne peut avoir moins de 2 caractères")]
public string Info2 { get; set; }
[Required(ErrorMessage = "Le paramètre info3 est requis")]
[MinLength(4, ErrorMessage = "Le paramètre info3 doit avoir 4 caractères exactement")]
[MaxLength(4, ErrorMessage = "Le paramètre info3 doit avoir 4 caractères exactement")]
public string Info3 { get; set; }
// ---------------------- вигляд --------------------------------
public string Erreurs { get; set; }
}
}
- рядки 8–28: модель дії з її обмеженнями цілісності. Ці поля також будуть частиною подання;
- рядок 31: властивість, властива саме моделі подання. Вона була виключена з моделі дії за допомогою анотації в рядку 5.
Ми створюємо нову дію [Action03] наступного вигляду:
// Дія03
public ViewResult Action03(ActionModel04 modèle)
{
modèle.Erreurs = getErrorMessagesFor(ModelState);
return View(modèle);
}
- рядок 2: [Action03] отримує модель дії типу [ActionModel04];
- рядок 5: і повертає як шаблон подання саме цей шаблон;
- рядок 4: доповнений інформацією [Erreurs];
Залишається лише створити представлення [/First/Action03.cshtml]:
![]() |
- у [1]: клацніть правою кнопкою миші на коді [Action03], а потім на [Ajouter une vue];
- у [2]: ім'я подання, запропоноване за замовчуванням;
- у [3]: вказати, що створюється строго типізований вигляд;
- у [4]: вибрати зі списку потрібний клас, у даному випадку клас [ActionModel04];
- у [5]: створений вигляд.
Ми надаємо погляду [Action03] той самий код, що й погляду [Action02]. Змінюються лише шаблон погляду (рядок 1) та заголовок сторінки (рядок 11):
@model Exemple_03.Models.ActionModel04
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Action03</title>
</head>
<body>
<h3>Informations du modèle de vue</h3>
<ul>
<li>Email : @Model.Email</li>
<li>Jour : @Model.Jour</li>
<li>Info1 : @Model.Info1</li>
<li>Info2 : @Model.Info2</li>
<li>Info3 : @Model.Info3</li>
<li>Erreurs : @Model.Erreurs</li>
</ul>
</body>
</html>
Тепер запустимо дію [Action03] без параметрів:
![]() |
Результати такі самі, як і раніше. Часто використовують один і той самий шаблон для дії та подання, оскільки шаблон подання часто переймає інформацію з шаблону дії. У такому разі використовують більш широкий шаблон, який може застосовуватися як для дії, так і для подання, яке вона генерує. Слід подбати про те, щоб виключити з прив’язки даних інформацію, яка не належить до моделі дії. Інакше досвідчений користувач міг би ініціалізувати частини моделі подання без нашого відома.
5.4. [Razor] — перші кроки
Тепер ми розглянемо деякі елементи представлень [Razor], головним чином інструкції foreach та if.
Припустимо, що ми хочемо відобразити список осіб у таблиці HTML. Шаблон подання може виглядати так: [ViewModel02]:
![]() |
namespace Exemple_03.Models
{
public class ViewModel02
{
public Personne[] Personnes { get; set; }
public ViewModel02()
{
Personnes = new Personne[] { new Personne { Nom = "Pierre", Age = 44 }, new Personne { Nom = "Pauline", Age = 12 } };
}
}
public class Personne
{
public string Nom { get; set; }
public int Age { get; set; }
}
}
- представлення моделі — це клас [ViewModel02], рядки 3–10;
- рядок 5: модель має масив осіб типу [Personne], визначений у рядках 12–16;
- рядки 6–10: конструктор моделі ініціалізує властивість [Personnes] з рядка 5 масивом із двох осіб.
Дія, що генерує цю модель на виході, буде такою: [Action04]:
// Дія04
public ViewResult Action04()
{
return View(new ViewModel02());
}
- рядок 2: дія не має вхідної моделі;
- рядок 4: вона переходить до свого виду за замовчуванням — екземпляра моделі [ViewModel02], яку ми щойно визначили.
Вигляд [Action04.cshtml] відображатиме модель [ViewModel02]:
![]() |
Код перегляду [Action04.cshtml] виглядає так:
@model Exemple_03.Models.ViewModel02
@using Exemple_03.Models
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Action04</title>
</head>
<body>
<table border="1">
<thead>
<tr>
<th>Nom</th>
<th>Age</th>
</tr>
</thead>
<tbody>
@foreach (Personne p in Model.Personnes)
{
<tr>
<td>@p.Nom</td>
<td>@p.Age</td>
</tr>
}
</tbody>
</table>
</body>
</html>
- рядок 1: шаблон подання;
- рядок 2: імпорт простору імен класу [Personne], що використовується в рядку 24;
- рядки 16–32: масив HTML, що відображає осіб з моделі;
- рядок 24: початок коду C# позначається символом @. Оператор [foreach] буде циклічно обробляти всіх осіб з моделі;
- рядки 26–27: символ < завершує виконання коду C# і починає виконання HTML. Потім знову символ @ для переходу в режим C# і запису імені особи. Далі знову символ <, який переводить у режим HTML;
- рядок 28: записується вік особи.
Виконання дії [Action04] дає такий результат:
![]() |
Інші елементи подання можуть заповнюватися з колекції: списки (з випадаючим меню чи без), перемикачі, прапорці. Розглянемо наступний новий приклад, який відображає випадаючий список.
Шаблон [ViewModel05] матиме такий вигляд:
![]() |
namespace Exemple_03.Models
{
public class ViewModel05
{
public Personne2[] Personnes { get; set; }
public int SelectedId { get; set; }
public ViewModel05()
{
Personnes = new Personne2[] {
new Personne2 { Id = 1, Prénom = "Pierre", Nom = "Martino" },
new Personne2 { Id = 2, Prénom = "Pauline", Nom = "Pereiro" },
new Personne2 { Id = 3, Prénom = "Jacques", Nom = "Alfonso" } };
SelectedId = 2;
}
}
public class Personne2
{
public int Id { get; set; }
public string Nom { get; set; }
public string Prénom { get; set; }
}
}
- рядок 18: клас [Personne2] із трьома властивостями;
- рядок 3: шаблон [ViewModel05] для подання;
- рядок 5: список осіб, які мають відображатися у випадаючому списку, у форматі [Prénom Nom];
- рядок 6: [Id] особи, яку слід вибрати у випадаючому списку;
- рядки 8–16: конструктор, який створює масив із трьох осіб (рядки 10–13) і встановлює [Id] особи, яка має з’явитися як вибрана.
У вікні [Action05.cshtml] відобразиться такий шаблон:
![]() |
Його код такий:
@model Exemple_03.Models.ViewModel05
@using Exemple_03.Models
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Action05</title>
</head>
<body>
<select>
@foreach (Personne2 p in Model.Personnes)
{
string selected = "";
if (p.Id == Model.SelectedId)
{
selected = "selected=\"selected\"";
}
<option value="@p.Id" @selected>@p.Prénom @p.Nom</option>
}
</select>
</body>
</html>
Характеристики випадаючого списку HTML були наведені в розділі 2.5.2.6. Нагадаємо їх:
Combo | <select size="1" name="cmbValeurs"> <option value="1">варіант1</option> <option selected="selected" value="2">варіант 2</option> <option value="3">варіант 3</option> </select> |
тег HTML | <select size=".." name=".."> <option [selected="selected"] value=”v”>...</option> ... </select> відображає у списку тексти, що містяться між тегами <option>...</option> |
атрибути | name="cmbValeurs": ім'я елемента управління. size="1": кількість видимих елементів списку. size="1" перетворює список на комбінований список. selected="selected": якщо це ключове слово вказано для елемента списку, він відображається у списку як вибраний. У нашому прикладі вище елемент списку choix2 відображається як вибраний елемент комбобокса, коли той з’являється вперше. value=”v”: якщо елемент вибрано користувачем, саме це значення [v] надсилається на сервер. За відсутності цього атрибута на сервер надсилається текст, що відображається та вибрано. |
Код у рядках 17–25 генерує теги <option>, які розміщуються в тезі <select> у рядку 16.
- рядок 17: відбувається перебір списку осіб у шаблоні;
- рядок 20: перевіряється, чи поточна особа є тією, яку потрібно виділити. Якщо так, готується текст selected="selected", який має бути вставлений у тег <option>;
- рядок 24: записується тег <option>.
Запустимо дію [Action05]:
![]() |
- у [1,2] особи відображаються у форматі [Prénom Nom];
- У [1,2] вибраною особою є та, у якої значення [Id] дорівнює 2.
Тепер розглянемо вихідний код HTML для наведеної вище сторінки:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Action05</title>
</head>
<body>
<select>
<option value="1" >Pierre Martino</option>
<option value="2" selected="selected">Pauline Pereiro</option>
<option value="3" >Jacques Alfonso</option>
</select>
</body>
</html>
- рядки 10–12: три теги <option>, згенеровані кодом [Razor];
- рядок 11: саме особа з [Id]=2 була обрана.
Цих двох прикладів, наведених вище, нам буде достатньо. Під час написання подання [Razor] слід втриматися від спокуси вбудовувати в нього логіку. Код на C# дозволив би це зробити. Однак у моделі MVC логіка має бути в дії або в нижніх шарах [Metier, DAO], але не у поданнях. Навіть дотримуючись моделі MVC, у представленні може опинитися багато логіки для обчислення проміжних значень. Це може означати, що використовувана модель недостатньо деталізована. Вона повинна містити кінцеві значення, які потрібні представленню, щоб йому не довелося обчислювати їх самостійно. Хороший вигляд — це вигляд, у якому міститься мінімум логіки і структура HTML залишається чіткою. Якщо вставити занадто багато коду C#, структура HTML може стати нечитабельною.
У наведеному вище прикладі випадаючий список може використовуватися користувачем, і нам потрібно буде дізнатися, яку особу він вибрав. Для цього нам потрібна форма.
5.5. Форма — перші кроки
Форма, яка відображатиметься користувачеві, матиме такий вигляд:
![]() |
Шаблоном подання буде шаблон [ViewModel05], який вже використовувався раніше. Дія, що відображатиме це подання, буде такою:
// Дія06-GET
[HttpGet]
public ViewResult Action06()
{
return View("Action06Get",new ViewModel05());
}
- рядок 2: дію можна викликати лише за допомогою команди HTTP GET;
- рядок 5: вигляд [/First/Action06Get.cshtml] буде відображено на основі екземпляра типу [ViewModel05].
Вигляд [/First/Action06Get.cshtml] буде таким:
![]() |
@model Exemple_03.Models.ViewModel05
@using Exemple_03.Models
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Action06-GET</title>
</head>
<body>
<h3>Action06 - GET</h3>
<p>Choisissez une personne</p>
<form method="post" action="/First/Action06">
<select name="personneId">
@foreach (Personne2 p in Model.Personnes)
{
string selected = "";
if (p.Id == Model.SelectedId)
{
selected = "selected=\"selected\"";
}
<option value="@p.Id" @selected>@p.Prénom @p.Nom</option>
}
</select>
<input name="valider" type="submit" value="Valider" />
</form>
</body>
</html>
Основні нововведення такі:
- рядок 18: щоб браузер міг передати інформацію, введену користувачем, нам потрібна форма. Її межі визначає тег <form> у рядках 18 та 31.
Тег HTML <form> було розглянуто в розділі 2.5.2.1. Нагадаємо його характеристики:
форма | |
тег HTML | <form name="..." method="..." action="...">...</form> |
атрибути | name="frmexemple": ім'я форми — необов'язкове method="..." : метод, який використовує браузер для надсилання на веб-сервер значень, зібраних у формі action="..." : URL, на яку будуть надіслані значення, зібрані у формі. Веб-форма оточена тегами <form>...</form>. Форма може мати ім’я (name="xx"). Це стосується всіх елементів управління, які можна знайти у формі. Мета форми — зібрати інформацію, введену користувачем за допомогою клавіатури/миші, та надіслати її на URL веб-сервера. Який саме? Той, що вказаний в атрибуті action="URL". Якщо цей атрибут відсутній, інформація буде надіслана на URL документа, в якому знаходиться форма. Веб-клієнт може використовувати два різні методи, що називаються POST та GET, для надсилання даних на веб-сервер. Атрибут method="méthode", причому method дорівнює GET або POST, у тезі <form> вказує браузеру, який метод слід використовувати для надсилання інформації, зібраної у формі, на URL, вказаний атрибутом action="URL". Якщо атрибут method не вказано, за замовчуванням використовується метод GET. |
- рядок 18: бачимо, що значення форми будуть надіслані до URL [/First/Action06] за допомогою команди HTTP POST;
- рядок 30: форма повинна мати кнопку типу [submit]. Саме вона ініціює надсилання введених значень до URL, вказаного атрибутом [action] тегу <form>.
Що саме передасть браузер, коли користувач натисне на кнопку [Valider]? Це було пояснено в розділі 2.5.3.1. Нагадаємо, що там було сказано:
елемент управління HTML | візуальний | повернуте значення (значення) |
<input type="radio" value="Так" name="R1"/>Так <input type="radio" name="R1" value="ні" checked="checked"/>Ні | R1=Так - значення атрибута value радіо-кнопки, яку користувач позначив. | |
<input type="checkbox" name="C1" value="один"/>1 <input type="checkbox" name="C2" value="два" checked="checked"/>2 <input type="checkbox" name="C3" value="три"/>3 | C1=один C2=два - значення атрибутів value полів, позначених користувачем | |
<input type="text" name="txtSaisie" size="20" value="кілька слів"/> | txtSaisie=програмування+Веб - текст, введений користувачем у поле введення. Пробіли замінено на знак + | |
<input type="password" name="txtMdp" size="20" value="unMotDePasse"/> | txtMdp=цесекрет - текст, введений користувачем у поле введення | |
<textarea rows="2" name="areaSaisie" cols="20"> рядок1 рядок 2 рядок3 </textarea> | areaSaisie=основи+програмування+Web%0D%0A веб-програмування+ - текст, введений користувачем у поле введення. %OD%OA — це позначка кінця рядка. Пробіли замінено на знак + | |
<select size="1" name="cmbValeurs"> <option value='1'>варіант1</option> <option selected="selected" value='2'>варіант2</option> <option value='3'>варіант3</option> </select> | cmbValeurs=3 - атрибут [value] елемента, вибраного користувачем | |
<select size="3" name="lst1"> <option selected="selected" value='1'>список1</option> <option value='2'>список2</option> <option value='3'>список3</option> <option value='4'>список4</option> <option value='5'>список5</option> </select> | ![]() | lst1=3 - атрибут [value] елемента, вибраного користувачем |
<select size="3" name="lst2" multiple="multiple"> <option selected="selected" value='1'>список1</option> <option value='2'>список2</option> <option selected="selected" value='3'>список3</option> <option value='4'>список4</option> <option value='5'>список5</option> </select> | lst2=1 lst2=3 - атрибути [value] елементів, вибраних користувачем | |
<input type="submit" value="Надіслати" name="cmdRenvoyer"/> | cmdRenvoyer=Надіслати - ім'я та атрибут value кнопки, яка використовувалася для надсилання даних форми на сервер | |
<input type="hidden" name="secret" value="uneValeur"/> | secret=значення - атрибут value прихованого поля |
У нашій формі є два теги, які можуть надсилати значення:
<select name="personneId">
...
</select>
та
<input name="valider" type="submit" value="Valider" />
Якщо користувач вибере особу № 2, значення будуть надіслані у такому вигляді:
Назви параметрів відповідають атрибутам [name] тегів, до яких відноситься POST. Без цього атрибута теги не передають значення. Так, у наведеному вище прикладі можна було б опустити атрибут name="valider" кнопки [submit]. Передане значення — це атрибут [value] кнопки. У даному випадку ця інформація нас не цікавить. Іноді форми містять кілька кнопок типу [submit]. У такому разі важливо знати, яку саме кнопку натиснули. Тому до різних кнопок слід додати атрибут [name].
Тег <select> складається з низки тегів <option>:
<select name="personneId">
<option value="1" >Pierre Martino</option>
<option value="2" selected="selected">Pauline Pereiro</option>
<option value="3" >Jacques Alfonso</option>
</select>
Відправляється значення атрибута [value] вибраної опції. Якщо цей атрибут відсутній, відправляється текст, що відображається опцією, наприклад [Pierre Martino].
Рядок
буде опубліковано у наступному URL [/First/Action06]:
// Дія06-POST
[HttpPost]
public ViewResult Action06(ActionModel06 modèle)
{
return View("Action06Post",modèle);
}
Можливо, ви пам’ятаєте, що у нас вже була операція [Action06]:
// Дія06-GET
[HttpGet]
public ViewResult Action06()
{
return View("Action06Get",new ViewModel05());
}
Можливе існування двох дій з однаковою назвою за умови, що вони не обробляють однакові команди HTTP:
- [Action06] у рядку 3 обробляє POST (рядок 2);
- [Action06] у рядку c обробляє GET (рядок b).
Дія [Action06], яка керує POST, отримає такий рядок параметрів:
Нам потрібна модель дії для інкапсуляції цих значень. Це буде така модель [ActionModel06]:
![]() |
using System.ComponentModel.DataAnnotations;
namespace Exemple_03.Models
{
public class ActionModel06
{
[Required(ErrorMessage = "Le paramètre [personneId] est requis")]
public int PersonneId { get; set; }
[Required(ErrorMessage = "Le paramètre [valider] est requis")]
public string Valider { get; set; }
}
}
Дія [Action06] отримує цей шаблон і передає його без змін до наступного подання [Action06Post] (рядок 5 дії):
![]() |
@model Exemple_03.Models.ActionModel06
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Action06Post</title>
</head>
<body>
<h3>Action06 - POST</h3>
Valeurs postées :
<ul>
<li>ID de la personne sélectionnée : @Model.PersonneId</li>
<li>Commande utilisée : @Model.Valider</li>
</ul>
</body>
</html>
Шаблон відображається у рядках 18 та 19.
Розглянемо приклад:
![]() |
У [1] вибирається третя особа з [Id], що дорівнює 3. У [2] відправляється форма. У [3] — отримані значення. У [4,5] видно, що було викликано ту саму функцію URL: один раз — через GET та [4], а інший — через POST та [5]. У URL цього не видно.
У вікні, що відображається після POST, можна було б відображати nom та prénom вибраної особи замість її номера. Тому необхідно вдосконалити вигляд POST та його модель.
Для обробки цього випадку ми створюємо дію [Action07]. Ця дія повинна використовувати сесію користувача для зберігання списку осіб. Ми будемо дотримуватися моделі, розглянутої в розділі 4.10, яка дозволяє включити дані з областей дії [Application] та [Session] у модель дії.
Моделлю сесії буде наступний клас [SessionModel]:
![]() |
namespace Exemple_03.Models
{
public class SessionModel
{
public Personne2[] Personnes { get; set; }
}
}
- рядок 2: сесія збереже список осіб, що відображаються у випадаючому списку;
Нам потрібно пов’язати попередній тип [SessionModel] із байндером, який ми назвемо [SessionModelBinder]. Він буде таким самим, як описано на сторінці 82:
![]() |
using System.Web.Mvc;
namespace Exemple_03.Infrastructure
{
public class SessionModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
// повертає дані з області дії [Session]
return controllerContext.HttpContext.Session["data"];
}
}
}
Зв’язок між шаблоном [SessionModel] та його binder [SessionModelBinder] здійснюється в [Global.asax]:
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
...
// модельні зв’язувачі
ModelBinders.Binders.Add(typeof(SessionModel), new SessionModelBinder());
}
// Сесія
public void Session_Start()
{
Session["data"] = new SessionModel();
}
}
- рядок 8: зв’язок моделі з її байндером здійснюється у [Application_Start];
- рядок 13: екземпляр типу [SessionModel] додається до сесії, пов'язаної з ключем [data].
Після цього дія [Action07] виглядає наступним чином:
// Action07-GET
[HttpGet]
public ViewResult Action07(SessionModel session)
{
ViewModel05 modèleVue = new ViewModel05();
session.Personnes= modèleVue.Personnes;
return View("Action07Get", modèleVue);
}
- рядок 3: дія отримує тип [SessionModel], тобто дані області дії [Session], пов’язані з ключем [data];
- рядок 5: створюється модель подання;
- рядок 6: масив осіб поміщається в сесію. Він знадобиться в наступному запиті — POST. Протокол HTTP є безстатусним. Необхідно використовувати сесію, щоб зберегти дані між запитами. Сесія є індивідуальною для кожного користувача та управляється веб-сервером;
- рядок 7: відображається подання [Action07Get.cshtml]. Воно має такий вигляд:
@model Exemple_03.Models.ViewModel05
@using Exemple_03.Models
...
<body>
<h3>Action07 - GET</h3>
<p>Choisissez une personne</p>
<form method="post" action="/First/Action07">
....
</form>
</body>
</html>
Вона ідентична вже розглянутому представленню [Action06Get.cshtml]. Головна відмінність полягає у рядку 7: це URL, куди будуть відправлені значення з форми. Вони будуть оброблені наступною дією [Action07]:
// Дія07-POST
[HttpPost]
public ViewResult Action07(SessionModel session, ActionModel06 modèle)
{
Personne2 personne = session.Personnes.Where(p => p.Id == modèle.PersonneId).First<Personne2>();
string strPersonne = string.Format("{0} {1}", personne.Prénom, personne.Nom);
return View("Action07Post", (object)strPersonne);
}
- рядок 3: відправлені значення інкапсульовані в шаблон дії [ActionModel06], який вже використовувався раніше (нижче):
using System.ComponentModel.DataAnnotations;
namespace Exemple_03.Models
{
public class ActionModel06
{
[Required(ErrorMessage = "Le paramètre [personneId] est requis")]
public int PersonneId { get; set; }
[Required(ErrorMessage = "Le paramètre [valider] est requis")]
public string Valider { get; set; }
}
}
- рядок 3: перший параметр — це дані діапазону [Session], пов’язані з ключем [data];
- рядок 5: запит LINQ витягує особу з [Id], який було опубліковано;
- рядок 6: формується рядок символів, який має відображатися у вікні [Action07Post] (рядок 8);
- рядок 7: щоб викликати потрібний конструктор [View], потрібно перетворити тип [string] на [object].
Вигляд [Action07Post.cshtml] має такий вигляд:
@model string
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Action07-Post</title>
</head>
<body>
<h3>Action07-POST</h3>
Vous avez sélectionné [@Model].
</body>
</html>
- рядок 1: шаблон має тип [string];
- рядок 16: відображається рядок символів.
Ось приклад виконання:
![]() | ![]() |
5.6. Форма — повний приклад
У розділі 2.5.2.1 ми розглянули таку форму HTML:
1 ![]() |
Ми розглянемо дію [Action08Get], яка відображає (GET) цю форму, та дію [Action08Post], яка обробляє (POST) значення, введені користувачем. Класична схема.
Шаблон подання [1], наведений вище, буде екземпляром класу [ViewModel08]. Цей клас буде одночасно:
- моделлю подання, що генерується GET на основі дії [Action08Get];
- моделлю дії [Action08Post] для запиту POST.
![]() |
![]() |
5.6.1. Шаблон області дії [Application]
Припустимо, що елементи, які відображаються за допомогою перемикачів, прапорців та різних списків, є даними області дії [Application]. Це типовий випадок. Ця інформація надходить із конфігураційного файлу або бази даних, які використовуються під час запуску програми в методі [Application_Start] класу [Global.asax]. Цей метод реалізовано наступним чином:
protected void Application_Start()
{
....
// модельні зв’язувачі
ModelBinders.Binders.Add(typeof(SessionModel), new SessionModelBinder());
ModelBinders.Binders.Add(typeof(ApplicationModel), new ApplicationModelBinder());
// дані області дії [Application]
Application["data"] = new ApplicationModel();
}
- рядок 7: тип [ApplicationModel], який ми незабаром опишемо, пов’язаний із з’єднувачем даних [ApplicationModelBinder], який ми вже розглядали на сторінці 82;
- рядок 10: екземпляр типу [ApplicationModel] зареєстрований у словнику додатка, пов’язаний із ключем [data].
Клас [ApplicationModel] слугує для інкапсуляції всіх даних області дії [Application]. У цьому випадку він інкапсулює дані, які має відображати форма:
![]() |
namespace Exemple_03.Models
{
public class ApplicationModel
{
// колекції, що відображаються у формі
public Item[] RadioButtonFieldItems { get; set; }
public Item[] CheckBoxesFieldItems { get; set; }
public Item[] DropDownListFieldItems { get; set; }
public Item[] SimpleChoiceListFieldItems { get; set; }
public Item[] MultipleChoiceListFieldItems { get; set; }
// ініціалізація полів та колекцій
public ApplicationModel()
{
RadioButtonFieldItems = new Item[]{
new Item {Value="1",Label="oui"},
new Item {Value="2", Label="non"}
};
CheckBoxesFieldItems = new Item[]{
new Item {Value="1",Label="1"},
new Item {Value="2", Label="2"},
new Item {Value="3", Label="3"}
};
DropDownListFieldItems = new Item[]{
new Item {Value="1",Label="choix1"},
new Item {Value="2", Label="choix2"},
new Item {Value="3", Label="choix3"}
};
SimpleChoiceListFieldItems = new Item[]{
new Item {Value="1",Label="liste1"},
new Item {Value="2", Label="liste2"},
new Item {Value="3", Label="liste3"},
new Item {Value="4", Label="liste4"},
new Item {Value="5", Label="liste5"}
};
MultipleChoiceListFieldItems = new Item[]{
new Item {Value="1",Label="liste1"},
new Item {Value="2", Label="liste2"},
new Item {Value="3", Label="liste3"},
new Item {Value="4", Label="liste4"},
new Item {Value="5", Label="liste5"}
};
}
// елемент колекцій
public class Item
{
public string Label { get; set; }
public string Value { get; set; }
}
}
}
- рядки 45–49: елементи різних колекцій форми. [Label] — це текст, що відображається елементом форми, [Value] — значення, яке надсилається цим елементом при його виборі;
- рядок 6: набір, що відображається перемикачем;
- рядок 7: колекція, що відображається за допомогою прапорців;
- рядок 8: колекція, що відображається випадаючим списком;
- рядок 9: колекція, що відображається списком з одним варіантом вибору;
- рядок 10: колекція, що відображається списком з можливістю множинного вибору;
- рядки 13–43: ці колекції ініціалізуються конструктором класу без параметрів.
Різні колекції будуть заповнювати наступну форму:
![]() |
5.6.2. Шаблон дії [Action08Get]
Попередня форма відображатиметься за допомогою наступної дії [Action08Get]:
// Action08-GET
[HttpGet]
public ViewResult Action08Get(ApplicationModel application)
{
ViewBag.info = string.Format("Contrôleur={0}, Action={1}", RouteData.Values["controller"], RouteData.Values["action"]);
return View("Formulaire", new ViewModel08(application));
}
- рядок 2: [Action08Get] відповідатиме лише на команду [GET];
- рядок 3: вона отримує як параметр шаблон додатка, який ми щойно описали;
- рядок 5: вона ініціалізує інформацію в динамічному контейнері [ViewBag];
- рядок 6: вона відображає представлення [/First/Formulaire.cshtml] із моделлю [ViewModel08]. Ця модель буде тією самою, що й у формі, представленій раніше. Для цього до конструктора передається модель додатка, яка визначає елементи, що мають відображатися.
5.6.3. Шаблон подання [Formulaire]
Клас [ViewModel08] буде моделлю форми. Цей клас має такий вигляд:
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
using Exemple_03.Models;
namespace Exemple_03.Models
{
public class ViewModel08
{
// поля для введення даних
public string RadioButtonField { get; set; }
public string[] CheckBoxesField { get; set; }
public string TextField { get; set; }
public string PasswordField { get; set; }
public string TextAreaField { get; set; }
public string DropDownListField { get; set; }
public string SimpleChoiceListField { get; set; }
public string[] MultipleChoiceListField { get; set; }
// колекції, що відображаються у формі
public ApplicationModel.Item[] RadioButtonFieldItems { get; set; }
public ApplicationModel.Item[] CheckBoxesFieldItems { get; set; }
public ApplicationModel.Item[] DropDownListFieldItems { get; set; }
public ApplicationModel.Item[] SimpleChoiceListFieldItems { get; set; }
public ApplicationModel.Item[] MultipleChoiceListFieldItems { get; set; }
// конструктори
public ViewModel08()
{
}
public ViewModel08(ApplicationModel application)
{
// ініціалізація колекцій
RadioButtonFieldItems = application.RadioButtonFieldItems;
CheckBoxesFieldItems = application.CheckBoxesFieldItems;
DropDownListFieldItems = application.DropDownListFieldItems;
SimpleChoiceListFieldItems = application.SimpleChoiceListFieldItems;
MultipleChoiceListFieldItems = application.MultipleChoiceListFieldItems;
// ініціалізація полів
RadioButtonField = "2";
CheckBoxesField = new string[] { "2" };
TextField = "quelques mots";
PasswordField = "secret";
TextAreaField = "ligne1\nligne2";
DropDownListField = "2";
SimpleChoiceListField = "3";
MultipleChoiceListField = new string[] { "1", "3" };
}
}
}
- у формі є два типи елементів: ті, що відображаються, та ті, що підлягають введенню;
- рядки 20–24 визначають елементи, що відображаються. Це різні колекції форми. Їх можна знайти в шаблоні додатка (рядки 34–38);
- рядки 10–17: визначають поля для введення даних у формі;
- рядок 10: [RadioButtonField] отримає значення, відправлене наступними рядками форми:
<!-- перемикачі -->
<tr>
<td>Etes-vous marié(e)</td>
<td>
<input type="radio" name="RadioButtonField" value="1" />oui
<input type="radio" name="RadioButtonField" value="2" checked="checked"/>non
</td>
</tr>
Зверніть увагу на рядки 5 і 6: атрибут [name] обох перемикачів — це ім’я властивості, яка буде ініціалізована. У надісланих даних буде рядок такого вигляду:
param1=val1&RadioButtonField=2¶m2=val2
якщо користувач позначив опцію з назвою [non]. Фактично, саме атрибут [value] позначеної опції надсилається.
- рядок 11: [CheckBoxesField] отримає значення, надіслані наступними рядками форми:
<!-- прапорці -->
<tr>
<td>Cases à cocher</td>
<td>
<input type="checkbox" name="CheckBoxesField" value="1" />1
<input type="checkbox" name="CheckBoxesField" value="2" checked="checked"/>2
<input type="checkbox" name="CheckBoxesField" value="3" />3
</td>
Зверніть увагу на рядки 5 і 6: атрибут [name] прапорців є назвою властивості, яка буде ініціалізована. У надісланих даних буде знайдено рядок у такому вигляді:
param1=val1&CheckBoxesField=2&CheckBoxesField=3¶m2=val2
якщо користувач встановив прапорці з назвами [2] та [3]. Саме атрибут [value] відмічених опцій надсилається. Оскільки може бути відправлено кілька параметрів з однаковою назвою, [CheckBoxesField] є масивом значень, а не окремим значенням. Якщо жодне поле не відмічено, параметр [CheckBoxesField] буде відсутній у переданому рядку, і однойменна властивість моделі не буде ініціалізована. Це може створити незручності, як ми побачимо далі.
- рядок 12: [TextField] отримає значення, надіслане наступними рядками форми:
<!-- однорядкове текстове поле -->
<tr>
<td>Champ de saisie</td>
<td>
<input type="text" name="TextField" value="quelques mots" size="30" />
</td>
</tr>
У рядку 5 атрибут [name] поля введення є назвою властивості, яка буде ініціалізована. У надісланих даних ми знайдемо рядок такого вигляду:
param1=val1&TextField=abcdef¶m2=val2
якщо користувач ввів [abcdef] у поле введення.
- рядок 13: [PasswordField] отримає значення, введені в наступних рядках форми:
<!-- поле для введення пароля -->
<tr>
<td>Mot de passe</td>
<td>
<input type="password" name="PasswordField" value="secret" size="30" />
</td>
</tr>
У рядку 5 атрибут [name] поля введення є назвою властивості, яка буде ініціалізована. У надісланих даних буде знайдено рядок у такому вигляді:
param1=val1&PasswordField=abcdef¶m2=val2
якщо користувач ввів [abcdef] у поле введення.
- рядок 14: [TextAreaField] отримає значення, відправлене наступними рядками форми:
<!-- багаторядкове поле для введення тексту -->
<tr>
<td>Boîte de saisie</td>
<td>
<textarea name="TextAreaField" cols="40" rows="3">ligne1
ligne2</textarea>
</td>
</tr>
У рядку 5 атрибут [name] поля введення є назвою властивості, яка буде ініціалізована. У переданих даних буде знайдено рядок у такому вигляді:
param1=val1&TextAreaField=abcdef%0D%OAhijk¶m2=val2
якщо користувач ввів у полі введення [abcdef], за яким слідує перехід на новий рядок, а потім [ijk].
- рядок 15: [DropDownListField] отримає значення, надіслане наступними рядками форми:
<!-- випадаючий список -->
<tr>
<td>Liste déroulante</td>
<td>
<select name="DropDownListField">
<option value="1" >choix1</option>
<option value="2" selected="selected">choix2</option>
<option value="3" >choix3</option>
</select>
</tr>
У рядку 5 атрибут [name] тегу <select> є назвою властивості, яка буде ініціалізована. У надісланих даних міститиметься рядок у такому вигляді:
param1=val1&DropDownListField=1¶m2=val2
якщо користувач вибрав опцію [choix1]. У формі надсилається атрибут [value] вибраної опції.
- рядок 16: [SingleChoiceListField] отримає значення, надіслане наступними рядками форми:
<!-- список з одним варіантом вибору -->
<tr>
<td>Liste à choix unique</td>
<td>
<select name="SimpleChoiceListField" size="3">
<option value="1" >liste1</option>
<option value="2" >liste2</option>
<option value="3" selected="selected">liste3</option>
<option value="4" >liste4</option>
<option value="5" >liste5</option>
</select>
</tr>
У рядку 5 атрибут [name] тегу <select> є назвою властивості, яка буде ініціалізована. Саме атрибут [size="3"] забезпечує відсутність випадаючого списку. У переданих даних міститься рядок у такому вигляді:
param1=val1&SimpleChoiceListField=3¶m2=val2
якщо користувач вибрав опцію [liste3]. У відправлених даних міститься атрибут [value] вибраної опції. Параметр [SingleChoiceListField] може бути відсутнім у відправленому рядку, якщо жоден елемент не було вибрано.
- рядок 17: [MultipleChoiceListField] отримає значення, відправлені наступними рядками форми:
<!-- список з варіантами відповідей -->
<tr>
<td>Liste à choix multiple</td>
<td>
<select name="MultipleChoiceListField" size="3" multiple="multiple">
<option value="1" selected="selected">liste1</option>
<option value="2" >liste2</option>
<option value="3" selected="selected">liste3</option>
<option value="4" >liste4</option>
<option value="5" >liste5</option>
</select>
</tr>
У рядку 5 атрибут [name] тегу <select> — це ім’я властивості, яка буде ініціалізована. Саме атрибут [size="3"] забезпечує відсутність випадаючого списку, а атрибут [multiple] дозволяє користувачеві вибирати кілька елементів, утримуючи натиснутою клавішу [Ctrl]. У надісланих даних можна знайти рядок такого вигляду:
param1=val1&MultipleChoiceListField=1&MultipleChoiceListField=3¶m2=val2
якщо користувач вибрав опції [liste1] та [liste3]. У дані передається саме атрибут [value] вибраних опцій. Оскільки може бути відправлено кілька параметрів з однаковою назвою, [MultipleChoiceListField] є масивом значень, а не окремим значенням. Якщо жодне поле не позначено, параметр [MultipleChoiceListField] буде відсутній у відправленому рядку, і однойменна властивість моделі не буде ініціалізована.
Різні поля введення, представлені вище, отримають значення, надіслані через форму. Їх також можна ініціалізувати перед відправленням форми. Саме це було зроблено тут:
// ініціалізація полів
RadioButtonField = "2";
CheckBoxesField = new string[] { "2" };
TextField = "quelques mots";
PasswordField = "secret";
TextAreaField = "ligne1\nligne2";
DropDownListField = "2";
SimpleChoiceListField = "3";
MultipleChoiceListField = new string[] { "1", "3" };
Якби ці значення були отримані після відправлення форми POST, це означало б, що користувач:
- рядок 2: встановив прапорець [non] на перемикачі;
- рядок 3: встановив прапорець біля опції [2];
- рядок 4: ввів [quelques mots] у поле введення;
- рядок 5: ввів [secret] як пароль;
- рядок 6: ввести [ligne1\nligne2] у багаторядкове поле введення;
- рядок 7: вибрано варіант [choix2] зі списку, що розгортається;
- рядок 8: вибрано варіант [liste3] зі списку з одним варіантом вибору;
- рядок 9: вибрано варіанти [liste1] та [liste3] зі списку з можливістю вибору декількох варіантів;
Ми будемо вважати, що відбулася операція POST і що потрібно повернути форму в тому вигляді, в якому вона була заповнена. Саме так відбувається, коли користувачеві повертається форма з помилками. Вона повертається в тому вигляді, в якому була заповнена.
5.6.4. Вигляд [Formulaire]
Вигляд [/First/Formulaire.cshtml] відображає форму:
![]() |
@model Exemple_03.Models.ViewModel08
@using Exemple_03.Models
@{
Layout = null;
}
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Formulaire</title>
</head>
<body>
<form method="post" action="Action08Post">
<h2>Formulaire ASP.NET MVC</h2>
<h3>Affiché par : @ViewBag.info</h3>
<table>
<thead></thead>
<tbody>
<!-- перемикачі -->
<tr>
<td>Etes-vous marié(e)</td>
<td>
@foreach (ApplicationModel.Item item in @Model.RadioButtonFieldItems)
{
string strChecked = item.Value == @Model.RadioButtonField ? "checked=\"checked\"" : "";
<input type="radio" name="RadioButtonField" value="@item.Value" @strChecked/>@item.Label
<text/>
}
</td>
</tr>
...
</tbody>
</table>
<input type="submit" value="Valider" />
</form>
</body>
</html>
- рядок 1: [ViewModel08] — це шаблон форми;
- рядок 12: тег <form> форми. Ця форма буде відправлена методом [POST] (атрибут method) на URL [/First/Action08Post] (атрибут action);
- рядок 33: кнопка типу [submit], яка служить для відправки форми;
- рядки 22–27: відображають перемикачі:
- рядок 22: здійснюється перегляд колекції, що відображається за допомогою перемикача;
- рядок 24: кнопка, у якої атрибут [value] має значення властивості [RadioButtonField], повинна бути позначена. Для цього вона повинна мати атрибут [checked="checked"];
- рядок 25: створення тегу <input type="radio"> із значенням [@item.Value] та назвою [@item.Label];
- рядок 26: тег <text/> не є визнаним тегом HTML. Він присутній для [Razor]. При його виявленні [Razor] згенерує перехід на новий рядок. Це не впливає на відображення форми, але впливає на згенерований код HTML. Теги <input type="radio"> тоді розміщуються на двох різних рядках замість одного. Це робить код більш читабельним, коли в браузері переглядається вихідний код відображеної сторінки;
Розглянемо інші елементи подання:
<!-- прапорці -->
<tr>
<td>Cases à cocher</td>
<td>
@{
foreach (ApplicationModel.Item item in @Model.CheckBoxesFieldItems)
{
string strChecked = @Model.CheckBoxesField.Contains(item.Value) ? "checked=\"checked\"" : "";
<input type="checkbox" name="CheckBoxesField" value="@item.Value" @strChecked/>@item.Label
<text/>
}
}
</td>
- рядок 6: ми перебираємо колекцію, що відображається за допомогою прапорців;
- рядок 8: для комірки, яка має атрибут [value], має бути встановлено прапорець для одного з значень властивості [CheckBoxesField]. Для цього вона повинна мати атрибут [checked="checked"]. Використовується вираз LINQ, який дозволяє визначити, чи міститься певне значення в масиві;
- рядок 25: створення тегу <input type="checkbox"> із значенням [@item.Value] та назвою [@item.Label];
<!-- однорядкове текстове поле -->
<tr>
<td>Champ de saisie</td>
<td>
<input type="text" name="TextField" value="@Model.TextField" size="30" />
</td>
</tr>
<!-- поле для введення пароля -->
<tr>
<td>Mot de passe</td>
<td>
<input type="password" name="PasswordField" value="@Model.PasswordField" size="30" />
</td>
</tr>
<!-- поле для введення багаторядкового тексту -->
<tr>
<td>Boîte de saisie</td>
<td>
<textarea name="TextAreaField" cols="40" rows="3">@Model.TextAreaField</textarea>
</td>
</tr>
- рядки 5, 12: атрибуту [value] тегу присвоюється значення з шаблону;
- рядок 19: те саме, але з іншим синтаксисом.
<!-- випадаючий список -->
<tr>
<td>Liste déroulante</td>
<td>
<select name="DropDownListField">
@{
foreach (ApplicationModel.Item item in @Model.DropDownListFieldItems)
{
string strChecked = item.Value == @Model.DropDownListField ? "selected=\"selected\"" : "";
<option value="@item.Value" @strChecked>@item.Label</option>
}
}
</select>
</tr>
- рядок 7: перебираємо колекцію, що відображається у випадаючому списку;
- рядок 9: слід вибрати опцію, яка має атрибут [value] із значенням властивості [DropDownListField]. Для цього вона повинна мати атрибут [selected="selected"];
- рядок 25: створення тегу <option value="valeur">libellé</option> із значенням [@item.Value] та назвою [@item.Label];
<!-- список з одним варіантом вибору -->
<tr>
<td>Liste à choix unique</td>
<td>
<select name="SimpleChoiceListField" size="3">
@{
foreach (ApplicationModel.Item item in @Model.SimpleChoiceListFieldItems)
{
string strChecked = item.Value == @Model.SimpleChoiceListField ? "selected=\"selected\"" : "";
<option value="@item.Value" @strChecked>@item.Label</option>
}
}
</select>
</tr>
Пояснення таке саме, як і для випадаючого списку.
<!-- список з можливістю вибору декількох елементів -->
<tr>
<td>Liste à choix multiple</td>
<td>
<select name="MultipleChoiceListField" size="3" multiple="multiple">
@{
foreach (ApplicationModel.Item item in @Model.MultipleChoiceListFieldItems)
{
string strChecked = @Model.MultipleChoiceListField.Contains(item.Value) ? "selected=\"selected\"" : "";
<option value="@item.Value" @strChecked>@item.Label</option>
}
}
</select>
</tr>
- рядок 7: відбувається перебір колекції, що відображається у списку;
- рядок 9: слід вибрати опцію, яка має як атрибут [value] одне з значень властивості [MultipleChoiceListField]. Для цього вона повинна мати атрибут [selected="selected"]. Використовується вираз LINQ, який дозволяє визначити, чи міститься значення в масиві;
- рядок 10: генерація тегу <option value="valeur">libellé</option> із значенням [@item.Value] та назвою [@item.Label];
5.6.5. Обробка POST з форми
Ми бачили, що форма буде передана до дії [Action08Post]:
<form method="post" action="Action08Post">
Дія [Action08Post] виглядає наступним чином:
// Action08-POST
[HttpPost]
public ViewResult Action08Post(ApplicationModel application, FormCollection posted)
{
ViewBag.info = string.Format("Contrôleur={0}, Action={1}", RouteData.Values["controller"], RouteData.Values["action"]);
ViewModel08 modèle = new ViewModel08(application);
TryUpdateModel(modèle,posted);
return View("Formulaire", modèle);
}
- рядок 3: шаблон додатка передається як параметр разом із відправленими значеннями. Вони доступні у типі [FormCollection]. Значення відправленого параметра [RadioButtonField] отримується за допомогою виразу posted[" RadioButtonField"]. У результаті отримуємо рядок або покажчик null. Якщо написати posted[" CheckBoxesField"], отримаємо масив рядків або покажчик null;
- то чому б не написати:
public ViewResult Action08Post(ApplicationModel application, ViewModel08 posted)
Є дві причини:
- перша полягає в тому, що фреймворк створить екземпляр моделі [ViewModel08] за допомогою конструктора без параметрів, що призведе до того, що колекції моделі не будуть ініціалізовані;
- друга полягає в тому, що ми хочемо контролювати, що потрапляє в модель. Ми знаємо, що існує чотири можливі джерела для моделі: параметри GET, POST, використовуваного маршруту та файлу uploadé. У цьому випадку ми хочемо ініціалізувати модель виключно за допомогою значень, що передаються.
- рядок 6: створюємо екземпляр моделі, використовуючи відповідний конструктор;
- рядок 7: ініціалізуємо її за допомогою значень, що були відправлені; після цієї операції модель відповідає введеним користувачем даним;
- рядок 8: знову відображаємо форму. Користувач побачить її такою, якою її заповнив.
Розглянемо приклад:
![]() |
У [2] результат [POST] точно відображає те, що було введено в [1].
5.6.6. Обробка аномалій у POST
Ми вже зазначали, що якщо для полів [CheckBoxesField, SimpleChoiceListField, MultipleChoiceListField] не було відмічено або вибрано жодного значення, то відповідні параметри не входили до надісланого рядка, а отже, властивості моделі з такими самими іменами не ініціалізувалися.
Розглянемо такий приклад:
![]() |
- у полі [1] жодне поле не було позначено;
- у [2] [POST] повертає поле, позначене галочкою.
Пояснення таке:
- оскільки жодне поле не відмічено, параметр [CheckBoxesField] не входить до переліку відправлених значень;
- дія [Action08Post] виконується наступним чином:
[HttpPost]
public ViewResult Action08Post(ApplicationModel application, FormCollection posted)
{
ViewBag.info = ...
ViewModel08 modèle = new ViewModel08(application);
TryUpdateModel(modèle,posted);
return View("Formulaire", modèle);
}
- рядок 5: створюється екземпляр шаблону форми. При цьому використовуваний конструктор присвоює масив ["2"] властивості [CheckBoxesField];
- рядок 6: передані значення записуються в модель. Оскільки параметр [CheckBoxesField] не входить до переліку відправлених значень, однойменна властивість не отримує значення. Отже, вона зберігає своє значення ["2"], через що під час відображення поле № 2 позначено галочкою, хоча цього не повинно бути.
Цю проблему можна вирішити різними способами. Ми вирішили вирішити її в коді дії [Action08Post]:
// Action08-POST
[HttpPost]
public ViewResult Action08Post(ApplicationModel application, FormCollection posted)
{
ViewBag.info = string.Format("Contrôleur={0}, Action={1}", RouteData.Values["controller"], RouteData.Values["action"]);
ViewModel08 modèle = new ViewModel08(application);
TryUpdateModel(modèle,posted);
// обробка значень, що не були відправлені
if (posted["CheckBoxesField"] == null)
{
modèle.CheckBoxesField = new string[] { };
}
if (posted["SimpleChoiceListField"] == null)
{
modèle.SimpleChoiceListField = "";
}
if (posted["MultipleChoiceListField"] == null)
{
modèle.MultipleChoiceListField = new string[] { };
}
// відображення форми
return View("Formulaire", modèle);
}
- рядки 9–20: перевіряється, чи були відправлені певні параметри. Якщо ні, їх ініціалізують значенням, що відповідає відсутності введення користувачем. Перевірка не проводилася для випадаючого списку, у якому завжди вибрано один елемент, на відміну від інших списків.
Пропонуємо читачам випробувати цю нову версію.
5.7. Використання спеціалізованих методів для створення форм
5.7.1. Нова форма
Ми створюємо нову форму [Formulaire2.cshtml], яка генеруватиме форму, ідентичну попередній:
![]() |
Повернемося до коду, який використовується для створення випадаючого списку у формі:
<!-- випадаючий список -->
<tr>
<td>Liste déroulante</td>
<td>
<select name="DropDownListField">
@{
foreach (ApplicationModel.Item item in @Model.DropDownListFieldItems)
{
string strChecked = item.Value == @Model.DropDownListField ? "selected=\"selected\"" : "";
<option value="@item.Value" @strChecked>@item.Label</option>
}
}
</select>
</tr>
Цей код має два недоліки:
- найголовніший — через складність коду втрачається розуміння сутності компонента, в даному випадку випадаючого списку;
- рядок 5: якщо помилитися в імені властивості моделі, яку потрібно використовувати як атрибут [name], це стане помітно лише під час виконання.
ASP.NET MVC пропонує спеціалізовані методи, що називаються [HTML Helpers], які, як випливає з їхньої назви, призначені для спрощення генерації HTML, зокрема для форм. За допомогою цих класів попередній випадаючий список записується наступним чином:
<!-- випадаючий список -->
<tr>
<td>Liste déroulante</td>
<td>@Html.DropDownListFor(m => m.DropDownListField,
new SelectList(@Model.DropDownListFieldItems, "Value", "Label"))
</td>
</tr>
Список, що розгортається, генерується рядками 4–5. Код став значно простішим. Код HTML, згенерований для списку, що розгортається, виглядає так:
<!-- випадаючий список -->
<tr>
<td>Liste déroulante</td>
<td><select id="DropDownListField" name="DropDownListField"><option value="1">choix1</option>
<option selected="selected" value="2">choix2</option>
<option value="3">choix3</option>
</select></td>
</tr>
- рядок 4: атрибут [name] є правильним;
- рядки 4–6: варіанти згенеровано правильно, і вибрано правильний варіант.
Повернемося до коду, який згенерував ці рядки HTML:
@Html.DropDownListFor(m => m.DropDownListField, new SelectList(@Model.DropDownListFieldItems, "Value", "Label"))
- перший параметр — це функція lambda (це її назва), де m позначає модель подання, а m.DropDowListField — властивість цієї моделі. Генератор коду HTML використає ім’я цього властивості для генерації атрибутів [id] та [name] для елемента [select], який буде згенеровано. Якщо використовувати властивість, якої не існує, помилка виникне під час компіляції, а не під час виконання. Це покращення порівняно з попереднім рішенням, де помилки в іменах виявлялися лише під час виконання;
- другий параметр слугує для визначення колекції елементів, які будуть заповнювати випадаючий список. Клас [SelectList] дозволяє побудувати цю колекцію:
- його перший параметр — це довільна колекція елементів. У даному випадку це колекція типу [Item];
- другий параметр — це властивість елементів, яка надасть значення тегу <option>. У даному випадку це властивість [Value] класу [Item];
- його третій параметр — це властивість елементів, яка надасть текст тегу <option>. У даному випадку це властивість [Label] класу [Item];
- щоб визначити, яку опцію слід вибрати (атрибут selected), фреймворк діє так само, як і ми: він порівнює значення опції з поточним значенням властивості [DropDownListField].
Тепер розглянемо інші методи, якими ми можемо скористатися:
Радіо-кнопки
Новий код виглядає так:
<!-- перемикачі -->
<tr>
<td>Etes-vous marié(e)</td>
<td>
@{
foreach (ApplicationModel.Item item in @Model.RadioButtonFieldItems)
{
@Html.RadioButtonFor(m => m.RadioButtonField, @item.Value)@item.Label
<text/>
}
}
</td>
</tr>
Згенерований код HTML виглядає так:
<!-- перемикачі -->
<tr>
<td>Etes-vous marié(e)</td>
<td>
<input id="RadioButtonField" name="RadioButtonField" type="radio" value="1" />oui
<input checked="checked" id="RadioButtonField" name="RadioButtonField" type="radio" value="2" />non
</td>
</tr>
Використано метод [Html.RadioButtonFor]:
- перший параметр — це властивість моделі, яка буде пов’язана з перемикачем (атрибут [name]);
- другий параметр — це значення, яке слід присвоїти перемикачу (атрибут [value]).
Поля для позначення
Код змінюється наступним чином:
<!-- прапорці -->
<tr>
<td>Cases à cocher</td>
<td>
@{
@Html.CheckBoxFor(m=>m.CheckBoxField1) @Model.CheckBoxesFieldItems[0].Label
@Html.CheckBoxFor(m=>m.CheckBoxField2) @Model.CheckBoxesFieldItems[1].Label
@Html.CheckBoxFor(m=>m.CheckBoxField3) @Model.CheckBoxesFieldItems[2].Label
}
</td>
Для генерації прапорців використовується метод [Html.CheckBoxFor]:
Параметр — це булеве властивість моделі, яке буде пов’язане з прапорцем. Якщо [Propriété=true], прапорець буде встановлено. Якщо [Propriété=false], прапорець не буде встановлено. У будь-якому випадку значення атрибута [value] дорівнює true. Згенерований код HTML має такий вигляд:
<input id="Propriété" name="Propriété" type="checkbox" value="true" />
<input name="Propriété" type="hidden" value="false" />
- рядок 1: поле для позначення з атрибутом [value="true"];
- рядок 2: приховане поле (type=hidden) з тією ж назвою [Propriété], що й прапорець з атрибутом [value="false"]. Чому є два теги [input] з однаковою назвою? Існують два випадки:
- поле у рядку 1 відмічено. Тоді рядок параметрів, що надсилається, має вигляд: Властивість=true&Властивість=false (рядки 1 і 2). Оскільки властивість [Propriété] очікує лише одне значення, можна припустити, що фреймворк присвоює значення [true] властивості [Propriété]. Для цього йому достатньо було б виконати логічну операцію OU між отриманими значеннями;
- поле в рядку 1 не відмічено. Отже, надісланий рядок параметрів має вигляд «Властивість=false» (лише рядок 2), і тоді властивість [Propriété] отримує значення [false], що є правильним (поле не було відмічено).
Однорядкове поле введення
Новий код виглядає так:
<!-- однорядкове текстове поле -->
<tr>
<td>Champ de saisie</td>
<td>
@Html.TextBoxFor(m => m.TextField, new { size = "30" })
</td>
</tr>
Згенерований код HTML має такий вигляд:
<!-- однорядкове поле введення тексту -->
<tr>
<td>Champ de saisie</td>
<td>
<input id="TextField" name="TextField" size="30" type="text" value="quelques mots" />
</td>
</tr>
Використано такий метод:
@Html.TextBoxFor(m => m.TextField, new { size = "30" })
- перший параметр визначає властивість моделі, пов’язану з полем введення. Ім’я властивості буде використано в атрибутах [name] та [id] тегу <input>, що згенеровано, а його значення буде присвоєно атрибуту [value];
- другий параметр — це анонімний клас, що визначає певні атрибути згенерованого тегу HTML, у даному випадку атрибут [size].
Поле для введення пароля
Новий код виглядає так:
<!-- поле для введення пароля -->
<tr>
<td>Mot de passe</td>
<td>
@Html.PasswordFor(m => m.PasswordField, new { size = "15" })
</td>
</tr>
Згенерований код HTML має такий вигляд:
<!-- поле для введення пароля -->
<tr>
<td>Mot de passe</td>
<td>
<input id="PasswordField" name="PasswordField" size="15" type="password" />
</td>
</tr>
Використано такий метод:
@Html.PasswordFor(m => m.PasswordField, new { size = "15" })
Принцип роботи аналогічний принципу роботи методу [Html.TexBoxFor].
Багаторядкове поле введення
Новий код такий:
<!-- поле для введення багаторядкового тексту -->
<tr>
<td>Boîte de saisie</td>
<td>
@Html.TextAreaFor(m => m.TextAreaField, new { cols = "30", rows = "5" })
</td>
</tr>
Згенерований код HTML має такий вигляд:
<!-- поле для введення багаторядкового тексту -->
<tr>
<td>Boîte de saisie</td>
<td>
<textarea cols="30" id="TextAreaField" name="TextAreaField" rows="5">
ligne1
ligne2</textarea>
</td>
</tr>
Використано такий метод:
@Html.TextAreaFor(m => m.TextAreaField, new { cols = "30", rows = "5" })
Принцип роботи аналогічний принципу роботи методу [Html.TexBoxFor].
Список з одним варіантом вибору
Новий код такий:
<!-- список з одним варіантом вибору -->
<tr>
<td>Liste à choix unique</td>
<td>
@Html.DropDownListFor(m => m.SimpleChoiceListField, new SelectList(@Model.SimpleChoiceListFieldItems, "Value", "Label"), new { size = "3" })
</tr>
а згенерований код HTML має такий вигляд:
<!-- список з одним варіантом вибору -->
<tr>
<td>Liste à choix unique</td>
<td>
<select id="SimpleChoiceListField" name="SimpleChoiceListField" size="3">
<option value="1">liste1</option>
<option value="2">liste2</option>
<option selected="selected" value="3">liste3</option>
<option value="4">liste4</option>
<option value="5">liste5</option>
</select>
</tr>
Ми вже розглядали метод [Html.DropDownListFor]. Єдина відмінність полягає в третьому параметрі, який використовується для вказівки атрибута [size], відмінного від 1. Саме ця характеристика дозволяє перейти від випадаючого списку [size=1] до простого списку.
Список із кількома варіантами вибору
Новий код виглядає так:
<!-- список з можливістю вибору кількох варіантів -->
<tr>
<td>Liste à choix multiple</td>
<td>
@Html.ListBoxFor(m => m.MultipleChoiceListField, new SelectList(@Model.MultipleChoiceListFieldItems, "Value", "Label"), new { size = "5" })
</tr>
а згенерований код HTML має такий вигляд:
<!-- список з можливістю вибору кількох варіантів -->
<tr>
<td>Liste à choix multiple</td>
<td>
<select id="MultipleChoiceListField" multiple="multiple" name="MultipleChoiceListField" size="5">
<option selected="selected" value="1">liste1</option>
<option value="2">liste2</option>
<option selected="selected" value="3">liste3</option>
<option value="4">liste4</option>
<option value="5">liste5</option>
</select>
</tr>
Метод
@Html.ListBoxFor(m => m.MultipleChoiceListField, new SelectList(@Model.MultipleChoiceListFieldItems, "Value", "Label"), new { size = "5" })
працює так само, як метод [Html.DropDownListFor], за винятком того, що він генерує список із можливістю множинного вибору. Вибраними є ті параметри, значення яких (атрибут value) містяться в таблиці [MultipleChoiceListField].
Тег <form> також можна створити за допомогою методу:
@using (Html.BeginForm("Action09Post", "First"))
{
...
}
Згенерований код HTML має такий вигляд:
<form action="/First/Action09Post" method="post">
...
</form>
Метод
Html.BeginForm("Action09Post", "First")
має як перший параметр ім'я дії, а як другий — ім'я контролера.
5.7.2. Акції та модель
Форма буде сформована за допомогою наступної дії [Action09Get]:
// Action09-GET
[HttpGet]
public ViewResult Action09Get(ApplicationModel application)
{
ViewBag.info = string.Format("Contrôleur={0}, Action={1}", RouteData.Values["controller"], RouteData.Values["action"]);
return View("Formulaire2", new ViewModel09(application));
}
Вигляд, що формується у рядку 6, — це [Formulaire2], пов’язаний із такою моделлю [ViewModel09]:
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
using Exemple_03.Models;
namespace Exemple_03.Models
{
public class ViewModel09
{
// поля введення
public string RadioButtonField { get; set; }
public bool CheckBoxField1 { get; set; }
public bool CheckBoxField2 { get; set; }
public bool CheckBoxField3 { get; set; }
public string TextField { get; set; }
public string PasswordField { get; set; }
public string TextAreaField { get; set; }
public string DropDownListField { get; set; }
public string SimpleChoiceListField { get; set; }
public string[] MultipleChoiceListField { get; set; }
// колекції, що відображаються у формі
public ApplicationModel.Item[] RadioButtonFieldItems { get; set; }
public ApplicationModel.Item[] CheckBoxesFieldItems { get; set; }
public ApplicationModel.Item[] DropDownListFieldItems { get; set; }
public ApplicationModel.Item[] SimpleChoiceListFieldItems { get; set; }
public ApplicationModel.Item[] MultipleChoiceListFieldItems { get; set; }
// конструктори
public ViewModel09()
{
}
public ViewModel09(ApplicationModel application)
{
// ініціалізація колекцій
RadioButtonFieldItems = application.RadioButtonFieldItems;
CheckBoxesFieldItems = application.CheckBoxesFieldItems;
DropDownListFieldItems = application.DropDownListFieldItems;
SimpleChoiceListFieldItems = application.SimpleChoiceListFieldItems;
MultipleChoiceListFieldItems = application.MultipleChoiceListFieldItems;
// ініціалізація полів
RadioButtonField = "2";
CheckBoxField2 = true;
TextField = "quelques mots";
PasswordField = "secret";
TextAreaField = "ligne1\nligne2";
DropDownListField = "2";
SimpleChoiceListField = "3";
MultipleChoiceListField = new string[] { "1", "3" };
}
}
}
[ViewModel09] відрізняється від [ViewModel08] способом обробки прапорців. Замість таблиці з трьома прапорцями використано три окремі прапорці (рядки 11–13).
Форма буде оброблена наступною дією [Action09Post]:
// Action09-POST
[HttpPost]
public ViewResult Action09Post(ApplicationModel application, FormCollection posted)
{
ViewBag.info = string.Format("Contrôleur={0}, Action={1}", RouteData.Values["controller"], RouteData.Values["action"]);
ViewModel09 modèle = new ViewModel09(application);
TryUpdateModel(modèle, posted);
// обробка значень, що не були відправлені
if (posted["SimpleChoiceListField"] == null)
{
modèle.SimpleChoiceListField = "";
}
if (posted["MultipleChoiceListField"] == null)
{
modèle.MultipleChoiceListField = new string[] { };
}
// відображення форми
return View("Formulaire2", modèle);
}
Дія [Action09Post] ідентична дії [Action08Post], за винятком двох моментів:
- рядок 18: замість подання [Formulaire] використовується подання [Formulaire2];
- більше не передбачено обробки полів, які не були відмічені. Тепер це правильно обробляється методом [Html.CheckBoxFor].
5.8. Створення форми на основі метаданих моделі
Існують й інші методи генерації форми, крім наведених вище. Один із них полягає у прив’язуванні до поля моделі інформації, яка дозволить фреймворку MVC визначити, який тег введення даних слід згенерувати. Цю інформацію називають метаданими.
Розглянемо таку модель подання [ViewModel10]:
![]() |
using System;
using System.ComponentModel.DataAnnotations;
using System.Drawing;
namespace Exemple_03.Models
{
public class ViewModel10
{
[Display(Name="Text")]
[DataType(DataType.Text)]
public string Text { get; set; }
[Display(Name = "TextArea")]
[DataType(DataType.MultilineText)]
public string MultiLineText { get; set; }
[Display(Name = "Number")]
public int Number { get; set; }
[Display(Name = "Decimal")]
[UIHint("Decimal")]
public double Decimal { get; set; }
[Display(Name = "Tel")]
[DataType(DataType.PhoneNumber)]
public string Tel { get; set; }
[Display(Name = "Date")]
[DataType(DataType.Date)]
public DateTime Date { get; set; }
[Display(Name = "Time")]
[DataType(DataType.Time)]
public DateTime Time { get; set; }
[Display(Name = "HiddenInput")]
[UIHint("HiddenInput")]
public string HiddenInput { get; set; }
[Display(Name = "Boolean")]
[UIHint("Boolean")]
public bool Boolean { get; set; }
[Display(Name = "Email")]
[DataType(DataType.EmailAddress)]
public string Email{ get; set; }
[Display(Name = "Url")]
[DataType(DataType.Url)]
public string Url { get; set; }
[Display(Name = "Password")]
[DataType(DataType.Password)]
public string Password { get; set; }
[Display(Name = "Currency")]
[DataType(DataType.Currency)]
public double Currency { get; set; }
[Display(Name = "CreditCard")]
[DataType(DataType.CreditCard)]
public string CreditCard { get; set; }
// конструктор
public ViewModel10()
{
Text = "tra la la";
MultiLineText = "ligne1\nligne2";
Number = 4;
Decimal = 10.2;
Tel = "0617181920";
Date = DateTime.Now;
Time = DateTime.Now;
HiddenInput = "caché";
Boolean = true;
Email = "x@y.z";
Url = "http://istia.univ-angers.fr";
Password = "mdp";
Currency = 4.2;
CreditCard = "0123456789012345";
}
}
}
Метадані складаються з тегів [Display, DataType, UIHint].
Ця модель подання буде побудована за допомогою такої дії: [Action10Get]
// Action10-GET
[HttpGet]
public ViewResult Action10Get()
{
return View(new ViewModel10());
}
У рядку 5 вище задано, щоб стандартний вигляд дії [/First/Action10Get.cshtml ] відображав шаблон вигляду типу [ViewModel10]. Цей вигляд має такий вигляд:
![]() |
@model Exemple_03.Models.ViewModel10
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Action10Get</title>
</head>
<body>
<h3>Formulaire ASP.NET MVC - 2</h3>
@using (Html.BeginForm("Action10Post", "First"))
{
<table>
<thead>
<tr>
<th>LabelFor</th>
<th>EditorFor</th>
<th>DisplayFor</th>
</tr>
</thead>
<tbody>
<tr>
<td>@Html.LabelFor(m => m.Text)</td>
<td>@Html.EditorFor(m => m.Text)</td>
<td>@Html.DisplayFor(m => m.Text)</td>
</tr>
<tr>
<td>@Html.LabelFor(m => m.MultiLineText)</td>
<td>@Html.EditorFor(m => m.MultiLineText)</td>
<td>@Html.DisplayFor(m => m.MultiLineText)</td>
</tr>
<tr>
<td>@Html.LabelFor(m => m.Number)</td>
<td>@Html.EditorFor(m => m.Number)</td>
<td>@Html.DisplayFor(m => m.Number)</td>
</tr>
<tr>
<td>@Html.LabelFor(m => m.Decimal)</td>
<td>@Html.EditorFor(m => m.Decimal)</td>
<td>@Html.DisplayFor(m => m.Decimal)</td>
</tr>
<tr>
<td>@Html.LabelFor(m => m.Tel)</td>
<td>@Html.EditorFor(m => m.Tel)</td>
<td>@Html.DisplayFor(m => m.Tel)</td>
</tr>
<tr>
<td>@Html.LabelFor(m => m.Date)</td>
<td>@Html.EditorFor(m => m.Date)</td>
<td>@Html.DisplayFor(m => m.Date)</td>
</tr>
<tr>
<td>@Html.LabelFor(m => m.Time)</td>
<td>@Html.EditorFor(m => m.Time)</td>
<td>@Html.DisplayFor(m => m.Time)</td>
</tr>
<tr>
<td>@Html.LabelFor(m => m.HiddenInput)</td>
<td>@Html.EditorFor(m => m.HiddenInput)</td>
<td>@Html.DisplayFor(m => m.HiddenInput)</td>
</tr>
<tr>
<td>@Html.LabelFor(m => m.Boolean)</td>
<td>@Html.EditorFor(m => m.Boolean)</td>
<td>@Html.DisplayFor(m => m.Boolean)</td>
</tr>
<tr>
<td>@Html.LabelFor(m => m.Email)</td>
<td>@Html.EditorFor(m => m.Email)</td>
<td>@Html.DisplayFor(m => m.Email)</td>
</tr>
<tr>
<td>@Html.LabelFor(m => m.Url)</td>
<td>@Html.EditorFor(m => m.Url)</td>
<td>@Html.DisplayFor(m => m.Url)</td>
</tr>
<tr>
<td>@Html.LabelFor(m => m.Password)</td>
<td>@Html.EditorFor(m => m.Password)</td>
<td>@Html.DisplayFor(m => m.Password)</td>
</tr>
<tr>
<td>@Html.LabelFor(m => m.Currency)</td>
<td>@Html.EditorFor(m => m.Currency)</td>
<td>@Html.DisplayFor(m => m.Currency)</td>
</tr>
<tr>
<td>@Html.LabelFor(m => m.CreditCard)</td>
<td>@Html.EditorFor(m => m.CreditCard)</td>
<td>@Html.DisplayFor(m => m.CreditCard)</td>
</tr>
</tbody>
</table>
<input type="submit" value="Valider" />
}
</body>
</html>
Для кожної з властивостей моделі ми використовуємо метод:
- Html.LabelFor для відображення значення метаданих [DisplayName] властивості;
- Html.EditorFor — для генерації тегу HTML для введення значення властивості. Цей метод використовуватиме метадані [DataType] та [UIHint] цієї властивості;
- Html.DisplayFor для відображення значення властивості у форматі, вказаному в метаданих [DataType].
Ось приклад виконання у браузері Chrome:

Залежно від використовуваного браузера сторінки можуть відрізнятися. Справа в тому, що згенерований вигляд використовує нові теги, введені у версії 5 HTML, яка називається HTML5. Не всі браузери ще підтримують цю версію. У наведеному вище прикладі браузер Chrome підтримує її частково.
5.8.1. [POST] форми
[POST] форми обробляється наступною дією [Action10Post]:
// Action10-POST
[HttpPost]
public ContentResult Action10Post(ViewModel10 modèle)
{
string erreurs = getErrorMessagesFor(ModelState);
string texte = string.Format("Contrôleur={0}, Action={1}, valide={2}, erreurs={3}", RouteData.Values["controller"], RouteData.Values["action"], ModelState.IsValid, erreurs);
return Content(texte, "text/plain", Encoding.UTF8);
}
- рядок 3: дія [Action10Post] має як вхідні дані відправлену форму;
- рядок 5: отримуються помилки перевірки цієї форми;
- рядок 6: готується текстова відповідь для клієнта;
- рядок 7: її надсилають.
Тепер розглянемо властивості шаблону [ViewModel10] по черзі та з’ясуємо, як пов’язані метадані впливають на згенерований HTML та перевірку полів введення.
5.8.2. Властивість [Text]
Визначення
[Display(Name="Text")]
[DataType(DataType.Text)]
public string Text { get; set; }
...
Text = "tra la la";
Вигляд
<tr>
<td>@Html.LabelFor(m => m.Text)</td>
<td>@Html.EditorFor(m => m.Text)</td>
<td>@Html.DisplayFor(m => m.Text)</td>
</tr>
Візуалізація
![]() |
HTML, згенеровано
<tr>
<td><label for="Text">Text</label></td>
<td><input class="text-box single-line" id="Text" name="Text" type="text" value="tra la la" /></td>
<td>tra la la</td>
</tr>
Коментарі
- метод [Html.LabelFor] згенерував тег <label> у рядку 2. Значенням атрибута [for] є ім'я властивості параметра методу [Html.LabelFor]
public string Text { get; set; }
Текст, що відображається між початком і кінцем тегу, є текстом метаданих
[Display(Name="Text")]
Метод [Html.LabelFor] завжди діє таким чином. Ми не будемо повертатися до цього питання щодо інших властивостей.
- Метод [Html.EditorFor] згенерував тег <input> у рядку 3. Зауважимо, що вона має атрибут [class], який пов’язує клас CSS [text-box single-line] із тегом. Значеннями атрибутів [id] та [name] є ім’я [Text] властивості «параметр» методу [Html.EditorFor]. Атрибут [type] отримав значення [text] через метадані
[DataType(DataType.Text)]
- метод [Html.DisplayFor] згенерував текст рядка 4. Це значення властивості параметра методу [Html.DisplayFor ]. На цей метод впливають метадані
[DataType(DataType.Text)]
, що призводить до відображення значення у вигляді неформатованого тексту.
5.8.3. Властивість [MultiLineText]
Визначення
[Display(Name = "TextArea")]
[DataType(DataType.MultilineText)]
public string MultiLineText { get; set; }
Вигляд
<tr>
<td>@Html.LabelFor(m => m.MultiLineText)</td>
<td>@Html.EditorFor(m => m.MultiLineText)</td>
<td>@Html.DisplayFor(m => m.MultiLineText)</td>
</tr>
Візуалізація
HTML, згенеровано
<tr>
<td><label for="MultiLineText">TextArea</label></td>
<td><textarea class="text-box multi-line" id="MultiLineText" name="MultiLineText">
ligne1
ligne2</textarea></td>
<td>ligne1
ligne2</td>
</tr>
Коментарі
- метод [Html.EditorFor] згенерував тег <textarea> у рядку 3. Слід зазначити, що вона має атрибут [class], який пов’язує клас CSS [text-box multi-line] із тегом. Значеннями атрибутів [id] та [name] є ім’я [MultiLineText] властивості «параметр» методу [Html.EditorFor]. Так відбувається завжди. Ми більше не будемо про це згадувати. Згенерований тег має вигляд <textarea> через метадані
[DataType(DataType.MultilineText)]
, яка вказувала, що властивість є багаторядковим текстом.
- Метод [Html.DisplayFor] згенерував текст рядків 4–5. Це значення властивості параметра методу [Html.DisplayFor ].
5.8.4. Властивість [Number]
Визначення
[Display(Name = "Number")]
public int Number { get; set; }
Вигляд
<tr>
<td>@Html.LabelFor(m => m.Number)</td>
<td>@Html.EditorFor(m => m.Number)</td>
<td>@Html.DisplayFor(m => m.Number)</td>
</tr>
Візуалізація
HTML, згенеровано
<tr>
<td><label for="Number">Number</label></td>
<td><input class="text-box single-line" data-val="true" data-val-number="Le champ Number doit être un nombre." data-val-required="Le champ Number est requis." id="Number" name="Number" type="number" value="4" /></td>
<td>4</td>
</tr>
Коментарі
- метод [Html.EditorFor] згенерував тег <input> у рядку 3 з атрибутом [type] типу [number]. Очевидно, просто тому, що властивість має тип [int]. Атрибути [data-val], [data-val-number] та [data-val-required] не розпізнаються HTML5. Вони використовуються фреймворком JavaScript для перевірки даних на стороні клієнта;
- метод [Html.DisplayFor] згенерував текст у рядку 4 — значення властивості.
Валідація
Атрибути [data-x] впливають на перевірку даних на стороні клієнта. Ось два приклади:
Вводимо неправильне число та перевіряємо:
![]() |
У наведеному вище прикладі перевірка відбулася на стороні клієнта. Форма не буде відправлена, доки помилка не буде виправлена.
Інший приклад: не вводимо нічого:
![]() |
У прикладі [1], наведеному вище, [Action10Post] сигналізує про помилку. Можливо, ви пам’ятаєте, що ми вже отримували таку поведінку, використовуючи атрибут [Required] для властивості, яку потрібно перевірити (див. стор. 69), у даному випадку — властивості [Number]. У цьому випадку нам не довелося цього робити.
5.8.5. Властивість [Decimal]
Визначення
[Display(Name = "Decimal")]
[UIHint("Decimal")]
public double Decimal { get; set; }
Вигляд
<tr>
<td>@Html.LabelFor(m => m.Decimal)</td>
<td>@Html.EditorFor(m => m.Decimal)</td>
<td>@Html.DisplayFor(m => m.Decimal)</td>
</tr>
Візуалізація
HTML, згенеровано
<tr>
<td><label for="Decimal">Decimal</label></td>
<td><input class="text-box single-line" data-val="true" data-val-number="Le champ Decimal doit être un nombre." data-val-required="Le champ Decimal est requis." id="Decimal" name="Decimal" type="text" value="10,20" /></td>
<td>10,20</td>
</tr>
Коментарі
- метод [Html.EditorFor] згенерував тег <input> у рядку 3 з атрибутом [type] типу [text]. Інші атрибути ідентичні тим, що були згенеровані для попередньої властивості [Number]. Метадані:
[UIHint("Decimal")]
призводить до того, що значення властивості відображається з двома десятковими знаками для обох методів [Html.EditorFor] та [Html.DisplayFor]
Валідація
На стороні клієнта не повідомляється про жодні помилки перевірки, на відміну від попереднього випадку. Помилка повідомляється лише дією [Action10Post]. І в цьому випадку десяткове число є обов’язковим, хоча немає потреби вказувати для нього атрибут [Required].
5.8.6. Властивість [Tel]
Визначення
[Display(Name = "Tel")]
[DataType(DataType.PhoneNumber)]
public string Tel { get; set; }
Вигляд
<tr>
<td>@Html.LabelFor(m => m.Tel)</td>
<td>@Html.EditorFor(m => m.Tel)</td>
<td>@Html.DisplayFor(m => m.Tel)</td>
</tr>
Візуалізація
HTML, згенеровано
<tr>
<td><label for="Tel">Tel</label></td>
<td><input class="text-box single-line" id="Tel" name="Tel" type="tel" value="0617181920" /></td>
<td>0617181920</td>
</tr>
Коментарі
- метод [Html.EditorFor] згенерував тег <input> у рядку 3 з атрибутом [type] типу [tel]. Це значення було згенеровано на основі метаданих:
[DataType(DataType.PhoneNumber)]
Тип [tel] для тегу <input> є новим порівняно з HTML5. Браузер Chrome обробив його як тег <input> з типом [text].
Валідація
Ні на стороні клієнта, ні на стороні сервера не виявлено жодних помилок валідації. Можна вводити будь-що.
5.8.7. Властивість [Date]
Визначення
[Display(Name = "Date")]
[DataType(DataType.Date)]
public DateTime Date { get; set; }
Вигляд
<tr>
<td>@Html.LabelFor(m => m.Date)</td>
<td>@Html.EditorFor(m => m.Date)</td>
<td>@Html.DisplayFor(m => m.Date)</td>
</tr>
Візуалізація
![]() |
HTML, згенеровано
<tr>
<td><label for="Date">Date</label></td>
<td><input class="text-box single-line" data-val="true" data-val-date="Le champ Date doit être une date." data-val-required="Le champ Date est requis." id="Date" name="Date" type="date" value="11/10/2013" /></td>
<td>11/10/2013</td>
</tr>
Коментарі
- метод [Html.EditorFor] згенерував тег <input> у рядку 3 з атрибутом [type] типу [date]. Це значення було згенеровано на основі метаданих:
[DataType(DataType.Date)]
Тип [date] для тегу <input> є новим типом HTML5. Браузер Chrome розпізнає його та дозволяє ввести дату за допомогою календаря. Крім того, введена дата відображається у форматі [jj/mm/aaaa], тобто Chrome адаптує формат дати до формату [locale], що використовується браузером.
- Метод [Html.DisplayFor] також записує дату у форматі [jj/mm/aaaa], що знову ж таки пов’язано з наявністю метаданих [Date].
Перевірка
На стороні клієнта виявлено недійсну дату [1], що перешкоджає передачі POST з форми на сервер.
![]() |
Відсутність дати не фіксується на стороні клієнта, але фіксується на стороні сервера [2].
5.8.8. Властивість [Time]
Визначення
[Display(Name = "Time")]
[DataType(DataType.Time)]
public DateTime Time { get; set; }
Вигляд
<tr>
<td>@Html.LabelFor(m => m.Time)</td>
<td>@Html.EditorFor(m => m.Time)</td>
<td>@Html.DisplayFor(m => m.Time)</td>
</tr>
Візуалізація
HTML, згенеровано
<tr>
<td><label for="Time">Time</label></td>
<td><input class="text-box single-line" data-val="true" data-val-required="Le champ Time est requis." id="Time" name="Time" type="time" value="11:17" /></td>
<td>11:17</td>
</tr>
Коментарі
- метод [Html.EditorFor] згенерував тег <input> у рядку 3 з атрибутом [type] типу [time]. Це значення було згенеровано на основі метаданих:
[DataType(DataType.Time)]
Тип [time] для тегу <input> є новим порівняно з HTML5. Браузер Chrome розпізнає його та дозволяє вводити час у форматі [hh:mm];
- метод [Html.DisplayFor] також записав час у форматі [hh:mm], знову ж таки через наявність метаданих [Time].
Перевірка
Технічно неможливо ввести недійсний час. Відсутність часу сигналізується на стороні сервера:
![]() |
5.8.9. Властивість [HiddenInput]
Визначення
[Display(Name = "HiddenInput")]
[UIHint("HiddenInput")]
public string HiddenInput { get; set; }
Вигляд
<tr>
<td>@Html.LabelFor(m => m.HiddenInput)</td>
<td>@Html.EditorFor(m => m.HiddenInput)</td>
<td>@Html.DisplayFor(m => m.HiddenInput)</td>
</tr>
Візуалізація
HTML, згенеровано
<tr>
<td><label for="HiddenInput">HiddenInput</label></td>
<td>caché<input id="HiddenInput" name="HiddenInput" type="hidden" value="приховано" /></td>
<td>caché</td>
</tr>
Коментарі
- метод [Html.EditorFor] згенерував тег <input> у рядку 3 з атрибутом [type] типу [hidden], тобто прихованим (але все ж відправленим) полем. Це значення було згенеровано на основі метаданих:
[UIHint("HiddenInput")]
- метод [Html.DisplayFor] сам записав значення прихованого поля.
5.8.10. Властивість [Boolean]
Визначення
[Display(Name = "Boolean")]
public bool Boolean { get; set; }
Вигляд
<tr>
<td>@Html.LabelFor(m => m.Boolean)</td>
<td>@Html.EditorFor(m => m.Boolean)</td>
<td>@Html.DisplayFor(m => m.Boolean)</td>
</tr>
Зображення
HTML, згенеровано
<tr>
<td><label for="Boolean">Boolean</label></td>
<td><input checked="checked" class="check-box" data-val="true" data-val-required="Le champ Boolean est requis." id="Boolean" name="Boolean" type="checkbox" value="true" /><input name="Boolean" type="hidden" value="false" /></td>
<td><input checked="checked" class="check-box" disabled="disabled" type="checkbox" /></td>
</tr>
Коментарі
- метод [Html.EditorFor] згенерував тег <input> у рядку 3 з атрибутом [type] типу [checkbox], тобто прапорцем. Це значення було згенеровано, оскільки властивість є булевою:
public bool Boolean { get; set; }
- метод [Html.DisplayFor] згенерував рядок 4, який також є прапорцем (атрибут type), але неактивним (атрибут disabled).
5.8.11. Властивість [Email]
Визначення
[Display(Name = "Email")]
[DataType(DataType.EmailAddress)]
public string Email{ get; set; }
Вигляд
<tr>
<td>@Html.LabelFor(m => m.Email)</td>
<td>@Html.EditorFor(m => m.Email)</td>
<td>@Html.DisplayFor(m => m.Email)</td>
</tr>
Візуалізація
HTML, згенеровано
<tr>
<td><label for="Email">Email</label></td>
<td><input class="text-box single-line" id="Email" name="Email" type="email" value="x@y.z" /></td>
<td><a href="mailto:x@y.z">x@y.z</a></td>
</tr>
Коментарі
- метод [Html.EditorFor] згенерував тег <input> у рядку 3 з атрибутом [type] типу [email]. Цей тип є новим у HTML5. Цей тип було згенеровано на основі таких метаданих:
[DataType(DataType.EmailAddress)]
Chrome, схоже, обробив цей тип як тип [text].
- Метод [Html.DisplayFor] згенерував рядок 4: посилання на адресу електронної пошти.
Перевірка
На стороні клієнта [1] повідомляється про недійсну адресу:
![]() |
Відсутність введених даних не спричиняє жодної помилки.
5.8.12. Властивість [Url]
Визначення
[Display(Name = "Url")]
[DataType(DataType.Url)]
public string Url { get; set; }
Вигляд
<tr>
<td>@Html.LabelFor(m => m.Url)</td>
<td>@Html.EditorFor(m => m.Url)</td>
<td>@Html.DisplayFor(m => m.Url)</td>
</tr>
Візуалізація
HTML, згенеровано
<tr>
<td><label for="Url">Url</label></td>
<td><input class="text-box single-line" id="Url" name="Url" type="url" value="http://istia.univ-angers.fr" /></td>
<td><a href="http://istia.univ-angers.fr">http://istia.univ-angers.fr</a></td>
</tr>
Коментарі
- метод [Html.EditorFor] згенерував тег <input> у рядку 3 з атрибутом [type] типу [url]. Цей тип є новим у HTML5. Він був згенерований на основі таких метаданих:
[DataType(DataType.Url)]
Chrome, схоже, обробляє цей тип як тип [text].
- Метод [Html.DisplayFor] згенерував рядок 4: посилання на URL.
Перевірка
На стороні клієнта виявлено недійсний URL — [1]:
![]() |
Відсутність введених даних не спричиняє жодної помилки.
5.8.13. Властивість [Password]
Визначення
[Display(Name = "Password")]
[DataType(DataType.Password)]
public string Password { get; set; }
Вигляд
<tr>
<td>@Html.LabelFor(m => m.Password)</td>
<td>@Html.EditorFor(m => m.Password)</td>
<td>@Html.DisplayFor(m => m.Password)</td>
</tr>
Візуалізація
HTML, згенеровано
<tr>
<td><label for="Password">Password</label></td>
<td><input class="text-box single-line password" id="Password" name="Password" type="password" value="mdp" /></td>
<td>mdp</td>
</tr>
Коментарі
- метод [Html.EditorFor] згенерував тег <input> у рядку 3 з атрибутом [type] типу [password]. Цей тип було згенеровано на основі метаданих:
[DataType(DataType.Password)]
- метод [Html.DisplayFor] згенерував рядок 4.
5.8.14. Властивість [Currency]
Визначення
[Display(Name = "Currency")]
[DataType(DataType.Currency)]
public double Currency { get; set; }
Вигляд
<tr>
<td>@Html.LabelFor(m => m.Currency)</td>
<td>@Html.EditorFor(m => m.Currency)</td>
<td>@Html.DisplayFor(m => m.Currency)</td>
</tr>
Візуалізація
HTML, згенеровано
<tr>
<td><label for="Currency">Currency</label></td>
<td><input class="text-box single-line" data-val="true" data-val-number="Le champ Currency doit être un nombre." data-val-required="Le champ Currency est requis." id="Currency" name="Currency" type="text" value="4,2" /></td>
<td>4,20 €</td>
</tr>
Коментарі
- метод [Html.EditorFor] згенерував тег <input> у рядку 3 з атрибутом [type] типу [text];
- метод [Html.DisplayFor] згенерував рядок 4 — число з двома десятковими знаками та символом валюти. Цей формат було використано через метадані:
[DataType(DataType.Currency)]
Перевірка
На стороні сервера повідомляється про недійсне значення [1] або відсутність значення [2]:
![]() |
5.8.15. Власність [CreditCard]
Визначення
[Display(Name = "CreditCard")]
[DataType(DataType.CreditCard)]
public string CreditCard { get; set; }
Вигляд
<tr>
<td>@Html.LabelFor(m => m.CreditCard)</td>
<td>@Html.EditorFor(m => m.CreditCard)</td>
<td>@Html.DisplayFor(m => m.CreditCard)</td>
</tr>
Візуалізація
HTML, згенеровано
<tr>
<td><label for="CreditCard">CreditCard</label></td>
<td><input class="text-box single-line" id="CreditCard" name="CreditCard" type="text" value="0123456789012345" /></td>
<td>0123456789012345</td>
</tr>
Коментарі
- метод [Html.EditorFor] згенерував тег <input> у рядку 3 з атрибутом [type] типу [text]. Метод [Html.DisplayFor] згенерував рядок 4. Тут не видно, що саме додають метадані:
[DataType(DataType.CreditCard)]
Перевірка
Перевірка не проводиться ні на стороні клієнта, ні на стороні сервера.
5.9. Валідація форми
Ми вже розглядали проблему перевірки моделі дії в розділі 4.5 та наступних розділах. Повернемося до цієї проблеми в контексті форми:
- як повідомити користувачеві про помилки введення даних;
- як проводити перевірку як на стороні клієнта, так і на стороні сервера, щоб швидше повідомляти користувачеві про помилки.
5.9.1. Серверна перевірка
Розглянемо таку модель:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Net.Mail;
namespace Exemple_03.Models
{
public class ViewModel11 : IValidatableObject
{
[Required(ErrorMessage = "Information requise")]
[Display(Name = "Chaîne d'au moins quatre caractères")]
[RegularExpression(@"^.{4,}$", ErrorMessage = "Information incorrecte")]
public string Chaine1 { get; set; }
[Display(Name = "Chaîne d'au plus quatre caractères")]
[Required(ErrorMessage = "Information requise")]
[RegularExpression(@"^.{1,4}$", ErrorMessage = "Information incorrecte")]
public string Chaine2 { get; set; }
[Required(ErrorMessage = "Information requise")]
[Display(Name = "Chaîne de quatre caractères exactement")]
[RegularExpression(@"^.{4,4}$", ErrorMessage = "Information incorrecte")]
public string Chaine3 { get; set; }
[Required(ErrorMessage = "Information requise")]
[Display(Name = "Nombre entier")]
public int Entier1 { get; set; }
[Display(Name = "Nombre entier dans l'intervalle [1,100]")]
[Required(ErrorMessage = "Information requise")]
[Range(1, 100, ErrorMessage = "Information incorrecte")]
public int Entier2 { get; set; }
[Display(Name = "Nombre réel")]
[Required(ErrorMessage = "Information requise")]
public double Reel1 { get; set; }
[Display(Name = "Nombre réel dans l'intervalle [10.2, 11.3]")]
[Required(ErrorMessage = "Information requise")]
[Range(10.2, 11.3, ErrorMessage = "Information incorrecte")]
public double Reel2 { get; set; }
[Display(Name = "Adresse mail")]
[Required(ErrorMessage = "Information requise")]
public string Email1 { get; set; }
[Display(Name = "Date sous la forme dd/jj/aaaa")]
[RegularExpression(@"\s*\d{2}/\d{2}/\d{4}\s*", ErrorMessage = "Information incorrecte")]
[Required(ErrorMessage = "Information requise")]
public string Regexp1 { get; set; }
[Display(Name = "Date postérieure à celle d'aujourd'hui")]
[Required(ErrorMessage = "Information requise")]
[DataType(DataType.Date)]
public DateTime Date1 { get; set; }
// перевірка
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
List<ValidationResult> résultats = new List<ValidationResult>();
// Дата 1
if (Date1.Date <= DateTime.Now.Date)
{
résultats.Add(new ValidationResult("Information incorrecte", new string[] { "Date1" }));
}
// Електронна пошта 1
try
{
new MailAddress(Email1);
}
catch
{
résultats.Add(new ValidationResult("Information incorrecte", new string[] { "Email1" }));
}
// повертає список помилок
return résultats;
}
}
}
Ця модель буде відображатися у такому вигляді: [Action11Get.cshtml]:
![]() |
@model Exemple_03.Models.ViewModel11
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Action11Get</title>
<link rel="stylesheet" href="~/Content/Site.css" />
</head>
<body>
<h3>Formulaire ASP.NET MVC – Validation 1</h3>
@using (Html.BeginForm("Action11Post", "First"))
{
<table>
<thead>
<tr>
<th>Type attendu</th>
<th>Valeur saisie</th>
<th>Message d'erreur</th>
</tr>
</thead>
<tbody>
<tr>
<td>@Html.LabelFor(m => m.Chaine1)</td>
<td>@Html.EditorFor(m => m.Chaine1)</td>
<td>@Html.ValidationMessageFor(m => m.Chaine1)</td>
</tr>
<tr>
<td>@Html.LabelFor(m => m.Chaine2)</td>
<td>@Html.EditorFor(m => m.Chaine2)</td>
<td>@Html.ValidationMessageFor(m => m.Chaine2)</td>
</tr>
<tr>
<td>@Html.LabelFor(m => m.Chaine3)</td>
<td>@Html.EditorFor(m => m.Chaine3)</td>
<td>@Html.ValidationMessageFor(m => m.Chaine3)</td>
</tr>
<tr>
<td>@Html.LabelFor(m => m.Entier1)</td>
<td>@Html.EditorFor(m => m.Entier1)</td>
<td>@Html.ValidationMessageFor(m => m.Entier1)</td>
</tr>
<tr>
<td>@Html.LabelFor(m => m.Entier2)</td>
<td>@Html.EditorFor(m => m.Entier2)</td>
<td>@Html.ValidationMessageFor(m => m.Entier2)</td>
</tr>
<tr>
<td>@Html.LabelFor(m => m.Reel1)</td>
<td>@Html.EditorFor(m => m.Reel1)</td>
<td>@Html.ValidationMessageFor(m => m.Reel1)</td>
</tr>
<tr>
<td>@Html.LabelFor(m => m.Reel2)</td>
<td>@Html.EditorFor(m => m.Reel2)</td>
<td>@Html.ValidationMessageFor(m => m.Reel2)</td>
</tr>
<tr>
<td>@Html.LabelFor(m => m.Email1)</td>
<td>@Html.EditorFor(m => m.Email1)</td>
<td>@Html.ValidationMessageFor(m => m.Email1)</td>
</tr>
<tr>
<td>@Html.LabelFor(m => m.Regexp1)</td>
<td>@Html.EditorFor(m => m.Regexp1)</td>
<td>@Html.ValidationMessageFor(m => m.Regexp1)</td>
</tr>
<tr>
<td>@Html.LabelFor(m => m.Date1)</td>
<td>@Html.EditorFor(m => m.Date1)</td>
<td>@Html.ValidationMessageFor(m => m.Date1)</td>
</tr>
</tbody>
</table>
<p>
<input type="submit" value="Valider" />
</p>
}
</body>
</html>
- рядок 12: посилання на таблицю стилів [Site.css]. За замовчуванням вона містить класи, що використовуються для виділення помилок при заповненні форми;
- рядки 18–25: таблиця з трьома стовпцями:
- у стовпці 1 текст відображається за допомогою методу [Html.LabelFor],
- у стовпці 2 відображається введений текст за допомогою методу [Html.EditorFor],
- у стовпці 3 відображаються можливі помилки введення за допомогою методу [Html.ValidationMessageFor];
Дія [Action11Get] призначена для відображення форми:
// Дія 11 — GET
[HttpGet]
public ViewResult Action11Get()
{
return View("Action11Get", new ViewModel11());
}
Дія [Action11Post] призначена для повторного відображення форми з можливими помилками введення:
// Дія11-POST
[HttpPost]
public ViewResult Action11Post(ViewModel11 modèle)
{
return View("Action11Get", modèle);
}
- рядок 3: шаблон [ViewModel11] створюється, а потім ініціалізується з відправленими значеннями. При цьому можуть виникнути помилки. Кожному помилковому властивості P шаблону відповідає повідомлення про помилку. Саме це повідомлення дозволяє отримати метод [Html.ValidationMessageFor] форми.
Ось приклад виконання:
![]() | ![]() |
Ось ще один приклад:
![]() |
Слід зауважити, що обидві дати є помилковими (сьогодні 11.10.2013), але про ці помилки не повідомляється. Ці помилки виявляються методом [Validate] моделі:
// перевірка
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
List<ValidationResult> résultats = new List<ValidationResult>();
// Дата 1
if (Date1.Date <= DateTime.Now.Date)
{
résultats.Add(new ValidationResult("Information incorrecte", new string[] { "Date1" }));
}
// Електронна пошта 1
try
{
new MailAddress(Email1);
}
catch
{
résultats.Add(new ValidationResult("Information incorrecte", new string[] { "Email1" }));
}
// Регулярний вираз 1
try
{
DateTime.ParseExact(Regexp1, "dd/MM/yyyy", CultureInfo.CreateSpecificCulture("fr-FR"));
}
catch
{
résultats.Add(new ValidationResult("Information incorrecte", new string[] { "Regexp1" }));
}
// видаємо список помилок
return résultats;
}
Метод [Validate] виконується лише тоді, коли всі перевірки за атрибутами пройдені успішно. Це ілюструє останній приклад:
![]() |
5.9.2. Перевірка на стороні клієнта
Усі попередні перевірки виконувалися на стороні сервера. Тому необхідний обмін даними між клієнтом і сервером, щоб користувач міг побачити свої помилки. Перевірка на стороні клієнта використовує код JavaScript, щоб повідомити користувача про його помилки якомога раніше і, у будь-якому разі, до виконання POST. Остання може відбутися лише тоді, коли всі виявлені помилки будуть виправлені.
Ми повертаємося до попереднього шаблону [ViewModel11], але тепер відображаємо його з таким виглядом [Action12Get.cshtml]:
![]() |
@model Exemple_03.Models.ViewModel11
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Action12Get</title>
<link rel="stylesheet" href="~/Content/Site.css" />
<script type="text/javascript" src="~/Scripts/jquery-1.8.2.min.js" ></script>
<script type="text/javascript" src="~/Scripts/jquery.validate.min.js" ></script>
<script type="text/javascript" src="~/Scripts/jquery.validate.unobtrusive.min.js" ></script>
</head>
<body>
<h3>Formulaire ASP.NET MVC - Validation 1</h3>
@using (Html.BeginForm("Action11Post", "First"))
{
<table>
<thead>
<tr>
<th>Type attendu</th>
<th>Valeur saisie</th>
<th>Message d'erreur</th>
</tr>
</thead>
<tbody>
...
</tbody>
</table>
<p>
<input type="submit" value="Valider" />
</p>
}
</body>
</html>
Примітка: рядок 13 — адаптуйте версію jQuery відповідно до вашої версії Visual Studio (див. нижче).
Для перевірки на стороні клієнта у файлі [Web.config] додатка має бути присутній рядок 3, наведений нижче.
<appSettings>
...
<add key="ClientValidationEnabled" value="true" />
</appSettings>
- рядки 1–4: розділ [appSettings] має бути безпосереднім дочірнім елементом розділу [configuration] у файлі [Web.config];
Вигляд [Action12Get] ідентичний попередньому вигляду [Action11Get], за винятком рядків 13–15. У цих рядках до вигляду додано скрипти JavaScript, необхідні для перевірки на стороні клієнта. Ці скрипти знаходяться у папці [Scripts] цього проєкту:
![]() |
Кожен скрипт має звичайну версію [.js] та мініфіковану версію [min.js]. Остання версія є меншою за розміром, але нечитабельною. Її використовують у виробничому середовищі. Читабельну версію використовують під час розробки.
Вигляд [Action12Get.cshtml] буде відображатися за допомогою наступної дії [Action12Get]:
// Дія12-GET
[HttpGet]
public ViewResult Action12Get()
{
return View("Action12Get", new ViewModel11());
}
Заповнена форма буде оброблена за допомогою наступної дії [Action12Post]:
// Дія12-POST
[HttpPost]
public ViewResult Action12Post(ViewModel11 modèle)
{
return View("Action12Get", modèle);
}
Давайте розглянемо, що це змінює, на прикладі:
![]() |
Як тільки ми вводимо символ у [1], з’являється повідомлення у [2], оскільки очікуване значення має містити щонайменше чотири символи. Таким чином, перевірка відбувається після введення кожного нового символу. Повідомлення про помилку зникає після введення четвертого символу. Після цього підтвердимо форму:
![]() |
URL [3] показує нам, що [POST] не відбулося. Однак натискання кнопки [Valider] запустило всі перевірки на стороні клієнта, і з’явилися нові повідомлення про помилки.
Давайте розглянемо, наприклад, код HTML, згенерований для першого запису:
<tr>
<td><label for="Chaine1">Chaîповинно містити щонайменше чотири символи</label></td>
<td><input class="text-box single-line" data-val="true" data-val-regex="Information incorrecte" data-val-regex-pattern="^.{4,}$" data-val-required="Information requise" id="Chaine1" name="Chaine1" type="text" value="" /></td>
<td><span class="field-validation-valid" data-valmsg-for="Chaine1" data-valmsg-replace="true"></span></td>
</tr>
- у рядку 3 міститься:
- повідомлення про помилку у разі відсутності введених даних [data-val-required],
- повідомлення про помилку у разі неправильного введення [data-val-regex],
- регулярний вираз для введеного рядка [data-val-regex-pattern];
- у рядку 4 — інші атрибути [data-x], що використовуються для відображення можливого повідомлення про помилку;
Атрибути [data-x] згенерованих тегів обробляються JavaScript-кодом, який ми вбудували у вид. Якщо його немає, ці атрибути просто ігноруються, і тоді перевірка на стороні клієнта не відбувається. Принцип роботи такий самий, як у попередньому прикладі. Звідси й походить термін [unobtrusive] для цієї техніки.
5.10. Управління навігаційними та дійовими посиланнями
Ми створимо два наступні види, щоб проілюструвати управління посиланнями у вигляді:
![]() |
- у [1] та [2] є два навігаційні посилання;
- у [3] є посилання дії, яке відправляє форму. Воно не призначене для навігації.
Сторінка 1 генерується наступним поданням [Action16Get.cshtml]:
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Action16Get</title>
<script>
function postForm() {
// отримуємо форму документа
var form = document.forms[0];
// відправка
form.submit();
}
</script>
</head>
<body>
<h3>Navigation - page 1</h3>
<h4>@ViewBag.info</h4>
@using (Html.BeginForm("Action16Post", "Second"))
{
@Html.Label("data", "Tapez un texte")
@Html.TextBox("data")
<a href="javascript:postForm()">Valider</a>
}
<p>
@Html.ActionLink("Page 2", "Action17Get", "Second")
</p>
</body>
</html>
- рядок 22: інформація, ініціалізована дією, яка буде видавати вигляд;
- рядки 23–28: форма;
- рядок 25: підпис для поля [data];
- рядок 26: поле введення з назвою [data];
- рядок 27: посилання типу [submit]. При натисканні на нього виконується функція JavaScript [postForm] (атрибут href). Вона визначена в рядках 12–17;
- рядок 14: отримується посилання на першу форму документа, тобто ту, що в рядку 23;
- рядок 16: ця форма відправляється. У підсумку все відбувається так, ніби ми натиснули кнопку типу [submit]. Форма відправляється до контролера та дії, вказаних у рядку 23;
- рядок 30: посилання для навігації. Згенерований код HTML має такий вигляд:
<a href="/Second/Action17Get">Page 2</a>
Використовується метод ActionLink(Текст, Дія, Контролер).
Сторінка 2 генерується за допомогою наступного подання [Action17Get.cshtml]:
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Action17Get</title>
</head>
<body>
<h3>Navigation - Page 2</h3>
<h4>@ViewBag.info</h4>
<p>
@Html.ActionLink("Page 1", "Action16Get", "Second")
</p>
</body>
</html>
Дії, що генерують ці подання, такі:
// Action16-GET
[HttpGet]
public ViewResult Action16Get()
{
ViewBag.info = string.Format("Contrôleur={0}, Action={1}", RouteData.Values["controller"], RouteData.Values["action"]);
return View("Action16Get");
}
// Action16-POST
[HttpPost]
public ViewResult Action16Post(string data)
{
ViewBag.info = string.Format("Contrôleur={0}, Action={1}, Data={2}", RouteData.Values["controller"], RouteData.Values["action"], data);
return View("Action16Get");
}
// Action17-GET
[HttpGet]
public ViewResult Action17Get()
{
ViewBag.info = string.Format("Contrôleur={0}, Action={1}", RouteData.Values["controller"], RouteData.Values["action"]);
return View();
}
- у рядку 6 дія [Action16Get] генерує представлення [Action16Get.cshtml], тобто сторінку 1 прикладу. Цьому представленню відповідає шаблон [ViewBag] (рядок 5);
- у рядку 19 дія [Action17Get] генерує вигляд [Action17Get.cshtml], тобто сторінку 2 прикладу. Цьому вигляду відповідає шаблон [ViewBag] (рядок 21);
- рядок 11: дія [Action16Post] обробляє POST з форми подання [Action16Get.cshtml]. Вона отримує параметр із назвою [data]. Нагадаємо, що це назва поля введення даних у формі;
- рядок 13: інформація записується в поле [ViewBag];
- рядок 14: відображається вікно [Action16Get.cshtml].
Пропонуємо читачеві випробувати цей приклад.































































