Здравствуйте, уважаемые читатели. В этой статье мы рассмотрим принципы работы с
календарями в ASP.NET MVC4, а также валидацию данных в этих контролах. Почему, спросите вы, я решил рассмотреть пример с контролами для работы с
датами? Ответ прост: с ними больше всего мороки. Как с этими проблемами бороться, мы рассмотрим в этой статье. Давайте
создадим приложение ASP.NET MVC 4 Web Application, как показано на
рисунке ниже, и назовем его ValidationDemo.Web.
Для
того чтобы не писать самим страницы, выберем простое Internet Application приложение, на
движке Razor,
для того чтобы проще было продемонстрировать наш пример.
После
этого перейдем в файл Index.cshtml и подправим его
следующим образом:
@{
ViewBag.Title = "Home Page";
}
<h2>Test
Data Validation</h2>
<div>
<div>
C
<input type="date" name="fromDate" value="@DateTime.Now"/>
</div>
<div>
по
<input type="date" name="toDate" value="@DateTime.Now"/>
</div>
</div>
После
запуска Google Chrome версии 29.0.1547.76 видим результат, аналогичный приведенному на экране.
К сожалению,
этот тип не работает в версиях IE9 и Mozilla Firefox
20
версии. После посмотрев на http://caniuse.com/#search=input%20type, я понял, почему этот контрол у
меня не заработал. Оказывается, все довольно просто: этот контрол не работает
нормально в данных версиях браузеров.
Поэтому
нужно искать другой путь для отображения контрола для вывода даты. Создадим модель данных ValidationDateControl, в
которой добавим два поля типа DateTime, для начальной даты и для конечной даты.
public class ValidationDateControl
{
[DataType(DataType.Date)]
[Display(Name = "С")]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime DateTimeStart { get; set; }
[DataType(DataType.Date)]
[Display(Name = "по")]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime DateTimeEnd { get; set; }
}
После этого перейдем в наш контроллер HomeController и изменим реализацию функции Index().
public ActionResult Index()
{
var data = new ValidationDateControl();
data.DateTimeStart
= DateTime.Now.AddDays(-3).Date;
data.DateTimeEnd
= DateTime.Now.Date;
return View("Index", data);
}
Затем необходимо немного модифицировать файл представления Index.cshtml, как показано ниже.
@{
ViewBag.Title = "Home Page";
}
<h2>Test
Data Validation</h2>
@model ValidationDemo.Web.Models.ValidationDateControl
<div>
<div>
@Html.LabelFor(m=>m.DateTimeStart)
@Html.TextBox("fromDate", Model.DateTimeStart.Date.ToString("yyyy-MM-dd"), new { @class = "text"
})
</div>
<div>
@Html.LabelFor(m=>m.DateTimeEnd)
@Html.TextBox("toDate", Model.DateTimeEnd.Date.ToString("yyyy-MM-dd"), new { @class = "text"
})
</div>
</div>
В
примере мы использовали возможности движка Razor. Для того чтобы более детально ознакомиться с html-представлением, которое будет сгенерировано нашим движком, мы можем посмотреть на вот этот хелп: HTML Helpers For Forms In Razor Web Pages. Но чтобы понять, что здесь написано, приведу
ниже трансляцию с Razor в html-разметку.
На
данном этапе у вас не должно возникнуть сложностей. После запуска тестового веб-приложения вы увидите просто текстовое поле с введенной датой, но у нас не
будет возможности изменить эту дату, кроме ручного ввода в поле. Поэтому для
этого поля воспользуемся контролом datapicker с библиотеки jquery-ui.
Для
того чтобы воспользоваться этим контролом, перейдем в файл Index.cshtml и
добавим следующий скрипт:
<script src="~/Scripts/jquery-1.8.2.js"></script>
<script type="text/javascript">
$(document).ready(function () {
var txt =
$('.text');
txt.datepicker({
dateFormat: "yy-mm-dd"
});
});
</script>
Полный
исходный код будет выглядеть следующим образом:
@{
ViewBag.Title = "Home Page";
}
<h2>Test
Data Validation</h2>
<script src="~/Scripts/jquery-1.8.2.js"></script>
@model ValidationDemo.Web.Models.ValidationDateControl
<div>
<div>
@Html.LabelFor(m=>m.DateTimeStart)
@Html.TextBox("fromDate", Model.DateTimeStart.Date.ToString("yyyy-MM-dd"), new { @class = "text"
})
</div>
<div>
@Html.LabelFor(m=>m.DateTimeEnd)
@Html.TextBox("toDate", Model.DateTimeEnd.Date.ToString("yyyy-MM-dd"), new { @class = "text"
})
</div>
</div>
<script type="text/javascript">
$(document).ready(function () {
var txt =
$('.text');
txt.datepicker({
dateFormat: "yy-mm-dd"
});
});
</script>
После
этого нам необходимо перейти в файл _Layout.cshtml и добавить следующую строчку
@Scripts.Render("~/bundles/jqueryui") после строки @Scripts.Render("~/bundles/jquery")
Ниже приведена часть реализации.
</footer>
@Scripts.Render("~/bundles/jquery")
@Scripts.Render("~/bundles/jqueryui")
@RenderSection("scripts", required: false)
</body>
</html>
После проделанной работы можем запустить пример и посмотреть результат.
Как видим с рисунка выше, контрол подтянулся, но не подтянулись сами css стили. У меня почему-то возникли проблемы со стилями для файла jquery-ui-1.8.24.js, и я решил
их обновить до более новой версии через Manage NuGet Packages к доступной версии
1.10.4, а также заодно обновить библиотеку jQuery до версии 2.1.0.
После
обновления заходим в файл _Layout.cshtml и добавленные строки @Scripts.Render("~/bundles/jqueryui") и
строку @Scripts.Render("~/bundles/jquery") перемещаем в самый
верх. Также необходимо добавить стили.
@Styles.Render("~/Content/css")
@Styles.Render("~/Content/themes/base/css")
@Scripts.Render("~/bundles/modernizr")
@Scripts.Render("~/bundles/jquery")
@Scripts.Render("~/bundles/jqueryui")
Полная реализация приведена ниже.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>@ViewBag.Title - Статистика Арт-Звіт</title>
<link href="~/favicon.ico" rel="shortcut icon" type="image/x-icon" />
<meta name="viewport" content="width=device-width" />
@Styles.Render("~/Content/css")
@Styles.Render("~/Content/themes/base/css")
@Scripts.Render("~/bundles/modernizr")
@Scripts.Render("~/bundles/jquery")
@Scripts.Render("~/bundles/jqueryui")
</head>
<body>
<header>
<div class="content-wrapper">
@*<div
class="float-left">
<p class="site-title">@Html.ActionLink("your logo
here", "Index", "Home")</p>
</div>*@
<div class="float-right">
@*<section
id="login">
@Html.Partial("_LoginPartial")
</section>*@
<nav>
<ul id="menu">
<li>@Html.ActionLink("Графік", "Chart", "Chart")</li>
<li>@Html.ActionLink("Статистика", "Statistic", "Statistic")</li>
</ul>
</nav>
</div>
</div>
</header>
<div id="body">
@RenderSection("featured", required: false)
<section class="content-wrapper main-content
clear-fix">
@RenderBody()
</section>
</div>
<footer>
<div class="content-wrapper">
<div class="float-left">
<p>© @DateTime.Now.Year - Панель статистики</p>
</div>
</div>
</footer>
@RenderSection("scripts", required: false)
</body>
</html>
По
сути, мы с вами выполнили всю черновую работу. Осталось только изменить старый скрипт в файле
Index.cshtml на новый.
<script src="~/Scripts/jquery-ui-1.10.4.js"></script>
Кроме
этой строки, больше ничего изменять не нужно. Запускаем наше приложение в IE для примера и
смотрим на результат.
Поскольку
контрол мы уже добавили, а также заставили его нормально отображаться, осталось
напоследок добавить валидацию для этих контролов. Для этого в нашу форму
добавим кнопку, после нажатия на которую через POST запрос отправим
данные на валидацию. После небольших изменений на форме ее код будет выглядеть
следующим образом:
@{
ViewBag.Title = "Home Page";
}
@model ValidationDemo.Web.Models.ValidationDateControl
<h2>Test
Data Validation</h2>
<script src="~/Scripts/jquery-ui-1.10.4.js"></script>
@using (Html.BeginForm())
{
<div>
<div>
@Html.LabelFor(m
=> m.DateTimeStart)
@Html.TextBox("DateTimeStart", Model.DateTimeStart.Date.ToString("yyyy-MM-dd"), new {@class = "text"})
@if (!ViewData.ModelState.IsValid)
{
if
(@ViewData.ModelState["DateTimeStart"].Errors.Count > 0)
{
<span class="field-validation-error">
@ViewData.ModelState["DateTimeStart"].Errors[0].ErrorMessage
</span>
}
}
</div>
<div>
@Html.LabelFor(m
=> m.DateTimeEnd)
@Html.TextBox("DateTimeEnd", Model.DateTimeEnd.Date.ToString("yyyy-MM-dd"), new {@class = "text"})
@if (!ViewData.ModelState.IsValid)
{
if
(@ViewData.ModelState["DateTimeEnd"].Errors.Count > 0)
{
<span class="field-validation-error">
@ViewData.ModelState["DateTimeEnd"].Errors[0].ErrorMessage
</span>
}
}
</div>
<div>
<input type="submit" name="calculate" value="Calculate"/>
</div>
</div>
}
<script type="text/javascript">
$(document).ready(function () {
var txt =
$('.text');
txt.datepicker({
dateFormat: "yy-mm-dd"
});
});
</script>
Переходим
в наш контроллер HomeController.cs и добавляем валидацию на пост запрос.
[HttpPost]
public ActionResult Index(ValidationDateControl model)
{
if
(model.DateTimeStart > DateTime.Now.Date)
{
ModelState.AddModelError("DateTimeStart", "Начальная
дата не может быть больше чем текущая");
}
if
(model.DateTimeEnd > DateTime.Now.Date)
{
ModelState.AddModelError("DateTimeEnd", "Конечная
дата не может быть больше чем текущая");
}
if
(model.DateTimeEnd < model.DateTimeStart)
{
ModelState.AddModelError("DateTimeStart", "Начальная
дата не может быть больше конечной");
}
var result
= model.DateTimeEnd - model.DateTimeStart;
if
(result.Days > 31)
{
ModelState.AddModelError("DateTimeStart", "Разница
между начальной и конечной датой не может превышать 31 день");
}
return View("Index", model);
}
Для примера запустим наше веб-приложение и поставим дату начала больше, чем
конечную дату. Результат вы можете увидеть на рисунке ниже.
Пример 1
Пример 2
Пример 3
Итоги
В приведенной статье мы рассмотрели, как можно подружить ASP.NET MVC 4 с jQuery UI для работы с датами, а также как
можно для этих контролов повесить валидацию данных. Надеюсь, что после
прочтения данной статьи у вас не возникнет проблем с данными контролами и с
валидацией в ASP.NET MVC 4 как таковой. Поскольку мне пришлось в свое
время разбирать эту тему и найти информацию обо всем в отдельном месте, мне не
посчастливилось, делюсь своими знаниями, которые приобрел, разбираясь с этой
непростой темой. Надеюсь, что статья будет вам полезной.