Часть 11 – Ненавязчивый JavaScript в ASP.NET MVC 3

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

Но проверить длину строки и соответствие её заданному формату, сравнить два строковых значения можно и на компьютере клиента. Такой подход выгоден для сервера, т.к. уменьшает число обращений к нему. Пользователь также будет в выигрыше, поскольку ошибки будут видны уже в момент ввода значений.

Проверка условий стандартных атрибутов на стороне клиента

Как реализовать такой вариант? На помощь вновь приходит ядро ASP.NET MVC. В него заложена возможность такого сценария для стандартных атрибутов проверки данных.

В одной из прошлых частей в файле web.config были сделаны изменения для блокировки некоторых возможностей ASP.NET MVC 3. Давайте вернем значение true для параметра ClientValidationEnabled, который разрешает проверку данных на стороне клиента:

<appSettings>
  <add key="ClientValidationEnabled" value="true"/> 
  <add key="UnobtrusiveJavaScriptEnabled" value="false"/> 
</appSettings>

Если теперь запустить проект и открыть форму ввода нового профиля, то … ничего не изменится. Дело в том, сейчас задействован механизм, унаследованный от предыдущих версий ASP.NET MVC. Для его функционирования требуется поддержка библиотек Microsoft Ajax.

Посмотрим на то, как раньше работала проверки на стороне клиента . Для этого откроем Представление UserProfiles\Create и добавим поддержку Microsoft Ajax для него:

@model MVCDemo.Models.NewUserProfileModel
@using MVCDemo.Resources.Views.UserProfiles;
@{
    ViewBag.Title = CreateRes.PageTitle;
}

<h2>@ViewBag.Title</h2>

<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>

<script src="@Url.Content("~/Scripts/MicrosoftAjax.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/MicrosoftMvcAjax.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/MicrosoftMvcValidation.js")" type="text/javascript"></script>

@using (Html.BeginForm()) {
.........

Запустим веб-приложение. Теперь тесты для правил, заданных стандартными атрибутами проверки данных, выполняются непосредственно в браузере пользователя. Разумеется, все, что реализовано дополнительными атрибутами, по прежнему обрабатывается на сервере.

Необходимо отметить, что каркас ASP.NET MVC следует идее обязательной проверки данных на стороне сервера. Это означает, она не отменяется при наличии тестов на стороне клиента. Последние, по сути, являются предварительными. Основные причины этого заключены в том, что в браузере может быть отключен JavaScript или сам запрос к серверу может быть подделан.

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

Ненавязчивый JavaScript (unobtrusive JavaScript)

Недостатки старого подхода

Перед тем как выяснить зачем используется UnobtrusiveJavaScriptEnabled и вернуть значение true, давайте посмотрим исходный код страницы с формой создан��я профиля. В нем можно обнаружить следующий блок (просто пробегитесь по нему не вчитываясь):

<script type="text/javascript">
//<![CDATA[
if (!window.mvcClientValidationMetadata) { window.mvcClientValidationMetadata = []; }
window.mvcClientValidationMetadata.push(
{"Fields":[{"FieldName":"Login",
"ReplaceValidationMessageContents":true,"ValidationMessageId":"Login_validationMessage",
"ValidationRules":[{"ErrorMessage":"You must enter a Login.","ValidationParameters":{},
"ValidationType":"required"}]},{"FieldName":"Password",
"ReplaceValidationMessageContents":true,"ValidationMessageId":"Password_validationMessage",
"ValidationRules":[{"ErrorMessage":"You must enter a Password.","ValidationParameters":{},
"ValidationType":"required"}]},{"FieldName":"ConfirmPassword",
"ReplaceValidationMessageContents":true,
"ValidationMessageId":"ConfirmPassword_validationMessage","ValidationRules":[]},
{"FieldName":"DisplayName","ReplaceValidationMessageContents":true,
"ValidationMessageId":"DisplayName_validationMessage",
"ValidationRules":[{"ErrorMessage":"You must enter a Display name.",
"ValidationParameters":{},"ValidationType":"required"}]},{"FieldName":"Email",
"ReplaceValidationMessageContents":true,"ValidationMessageId":"Email_validationMessage",
"ValidationRules":[{"ErrorMessage":"This string is too long (maximum 64 characters).",
"ValidationParameters":{"max":64},"ValidationType":"length"},
{"ErrorMessage":"You must enter a Email.","ValidationParameters":{},
"ValidationType":"required"},{"ErrorMessage":"Invalid email address.",
"ValidationParameters":{"pattern":
"[\\w\\.-]*[a-zA-Z0-9_]@[\\w\\.-]*[a-zA-Z0-9]\\.[a-zA-Z][a-zA-Z\\.]*[a-zA-Z]"},
"ValidationType":"regex"}]},{"FieldName":"Homepage","ReplaceValidationMessageContents":true,
"ValidationMessageId":"Homepage_validationMessage",
"ValidationRules":[{"ErrorMessage":"This string is too long (maximum 96 characters).",
"ValidationParameters":{"max":96},"ValidationType":"length"},
{"ErrorMessage":"Invalid website address.","ValidationParameters":
{"pattern":"(http(s)?://)?([\\w-]+\\.)+[\\w-]+(/[\\w- ;,./?%&=]*)?"},
"ValidationType":"regex"}]},{"FieldName":"AgreementAccepted",
"ReplaceValidationMessageContents":true,
"ValidationMessageId":"AgreementAccepted_validationMessage",
"ValidationRules":
[{"ErrorMessage":"The I have read the EULA and I agree with it field is required.",
"ValidationParameters":{},"ValidationType":"required"}]}],"FormId":"form0",
"ReplaceValidationSummary":false,"ValidationSummaryId":"validationSummary"});
//]]>
</script>

Данный код специально приведен полностью. Даже при невнимательном его просмотре хорошо видно, что он содержит все параметры стандартных атрибутов и их сообщения об ошибках. Легко заметить заданные регулярные выражения. Таким образом, все данные их атрибутов перенесены в виде блока JavaScript. Они используются библиотекой Microsoft Ajax для организации проверки на стороне клиента.

Многим не нравится такое решение. И дело не во внешнем виде. Например, некоторые мотивируют это оптимизацией под поисковые системы. С точки зрения разработчиков, недовольство может вызвать факт смешения структуры документа и поведения (вызовов функции). Так можно ли избавиться от этого?

Что такое ненавязчивый JavaScript

Задача ненавязчивого JavaScript как раз и состоит в разделении функциональности и разметки страницы. В этом случае необходимые данные размещаются в атрибутах HTML тегов. Страница при этом остается корректной с точки зрения браузеров, т.к. по стандарту они просто будут игнорировать незнакомые атрибуты. В дальнейшем, внешний скрипт может использовать размещенное таким образом значения для решения своих задач. Кроме того, воспринимая их как признаки он может, например, добавлять обработчики.

Чтобы стало более ясно, рассмотрим пример. Предположим в HTML коде есть следующая строка:

<input type="text" name="amount" onchange="updateTotalPrice(this);" />

Здесь в структуру добавлен вызов функции для обновления цены в зависимости от количества товара. Используя идеологию ненавязчивого JavaScript данный фрагмент можно переписать так:

<input type="text" name="amount" taskid="updateTotalPrice" />

Явный вызов функции был заменен на логическое выделение HTML элемента. При этом, после загрузки страницы должен быть запущен специальный скрипт. Он просмотрит всю структуру HTML документа и выполнит замену этой и других подобных отметок на обращения к соответствующим обработчикам.

Использование ненавязчивого JavaScript в ASP.NET MVC 3

Вернемся к файлу web.config и задействуем ненавязчивый JavaScript:

<appSettings>
  <add key="ClientValidationEnabled" value="true"/> 
  <add key="UnobtrusiveJavaScriptEnabled" value="true"/> 
</appSettings>

Теперь больше нет необходимости в Microsoft Ajax. Вместо него каркас ASP.NET MVC будет использовать библиотеку jQuery, которая уже была подключена к Представлению при его создании. Поэтому уберем добавленные три строки из UserProfiles\Create.

@model MVCDemo.Models.NewUserProfileModel
@using MVCDemo.Resources.Views.UserProfiles;
@{
    ViewBag.Title = CreateRes.PageTitle;
}

<h2>@ViewBag.Title</h2>

<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>

@using (Html.BeginForm()) {

Запустим проект и снова посмотрим на HTML код страницы добавления профиля. Теперь в нем нет списка значений в виде кода на JavaScript с блоком CDATA. Тогда где же все данные для проверки? Они перемещены в HTML-теги как атрибуты и их имена начинаются с приставки "data-val". Например, вот часть кода, описывающего поле ввода имени входа на сайт:

<div class="editor-label">
    <label for="Login">Login</label>
</div>
<div class="editor-field">
    <input class="text-box single-line" data-val="true" 
        data-val-required="You must enter a Login." id="Login" 
        name="Login" type="text" value="" />
    <span class="field-validation-valid" data-valmsg-for="Login" 
        data-valmsg-replace="true"></span>
</div>

Что дает такое решение? HTML код становится менее зависим от функциональной части. Например, в текущей реализации ASP.NET MVC 3 для выполнения проверок вводимых значений используется jQuery. В приведенном HTML коде нет обращений к её функциям. Зато есть отметки, которые указывают какие поля и как надо проверять. Теперь, при необходимости, можно будет легко заменить jQuery на другую библиотеку. Т.е. любой скрипт может получить сохраненные таким образом данные, используя стандартные методы для работы со структурой страницы.

Настройка параметров для отдельного Представления

Рассмотренные выше конфигурационные параметры были заданы через файл конфигурации. Действие такие настройки распространяется на всё веб-приложение. Однако можно указать значения для конкретного Представления, используя следующие вызовы методов в его коде:

@{Html.EnableClientValidation(true);}
@{Html.EnableUnobtrusiveJavaScript(true);}

Теперь предварительная проверка части правил происходит на стороне клиента. В следующей части перенесем туда и остальные тесты.

Комментарии (10) -

Владимир 14.03.2011 13:26:04

Спасибо за отличную серию статей.

Пожалуйста. Приятно слышать что статьи приносят пользу в изучении данной технологии.

unsafeCode 24.03.2011 18:26:27

Действительно очень полезно - все предельно ясно, без "воды", по делу.
Спасибо за ваш труд )

Пожалуйста.

Виктор 29.03.2011 14:19:03

гвт тока не хватает...А так МС с первого vdw нужно было не велосипед изобретать(asp.net ajax), а более мощным инстументом воспользоваться. Спасибо за статью

Виктор 29.03.2011 14:19:18

* mvc

Алексей 13.04.2011 20:39:17

Спасибо за ваши статьи, очень приятно и полезно читать

Андрей, спасибо, доступно объясняете материал!

Андрей, у меня такой вопрос:
Здесь написано что в MVC3 использование MicrosoftAjax.js, MicrosoftMvcAjax.js и MicrosoftMvcValidation.js считается устаревшим.
stackoverflow.com/.../are-microsoftajax-js-microsoftmvcajax-js-and-microsoftmvcvalidation-js-obsolete

Будет работать без них (при одключенных jquery.validate.min.js и jquery.validate.unobtrusive.min.js) такая аякс-форма?
using (Ajax.BeginForm(...)) {}

@ Илья: Нет, т.к. jquery.validate.min.js и jquery.validate.unobtrusive.min.js это скрипты для валидации данных. И я бы не советовал использовать старые методы, т.к. все это сейчас легко пишется на JS + jQuery.

Pingbacks and trackbacks (3)+

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