Главная страница   /   13.3. Валидация значений форм (Pro jQuery

Pro jQuery

Pro jQuery

Адам Фриман

13.3. Валидация значений форм

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

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

Вторая причина заключается в том, что пользователь не желает давать вам данные, которые вы запросили, и просто пытается заполнить форму как можно быстрее. Такие пользователи пишут в форме все, что угодно, лишь бы побыстрее перейти к следующему этапу. Если у вас есть много пользователей, чей e-mail адрес выглядит вот так a@a.com, тогда вы понимаете, что это проблема.

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

И, наконец, пользователь может действительно сделать ошибку, обычно описку. Например, я быстро, но не особо аккуратно набираю текст, и часто я набираю мою фамилию как Freman, вместо Freeman, пропуская одну е.

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

Я не хочу произносить длинную и напыщенную речь о дизайне веб форм, но я хочу сказать, что лучший способ решить эту задачу – это сосредоточиться на том, что хочет получить пользователь. И если все идет не так, попытайтесь увидеть проблему (и пути ее решения) глазами пользователя. Ваши пользователи не знают, как вы выстроили свою систему, они ничего не знают о вашем бизнес процессе; они просто хотят, чтобы что-то у них получилось. Любой будет рад и счастлив, если вы сфокусируетесь на задаче, которую пользователь пытается выполнить, вместо того, чтобы бессмысленно пугать его, если он не предоставит требуемые вами данные.

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

Внимание

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

Плагин Validation можно загрузить с http://bassistance.de/jquery-plugins/jquery-plugin-validation или использовать версию, которую я включил в исходный код для этой книги (доступный на Apress.com). В листинге 13-7 показано использование этого плагина.

Совет

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

Листинг 13-7: Использование плагина валидации формы
<!DOCTYPE html>
<html>
<head>
	<title>Example</title>
	<script src="jquery-1.7.js" type="text/javascript"></script>
	<script src="jquery.tmpl.js" type="text/javascript"></script>
	<script src="jquery.validate.js" type="text/javascript"></script>
	<link rel="stylesheet" type="text/css" href="styles.css" />
	<style type="text/css">
		div.errorMsg { color: red; }
		.invalidElem { border: medium solid red; }
	</style>
	<script type="text/javascript">
		$(document).ready(function () {
			var data = [
				{ name: "Astor", product: "astor", stocklevel: "10", price: "2.99" },
				{ name: "Daffodil", product: "daffodil", stocklevel: "12", price: "1.99" },
				{ name: "Rose", product: "rose", stocklevel: "2", price: "4.99" },
				{ name: "Peony", product: "peony", stocklevel: "0", price: "1.50" },
				{ name: "Primula", product: "primula", stocklevel: "1", price: "3.12" },
				{ name: "Snowdrop", product: "snowdrop", stocklevel: "15", price: "0.99" },
			];

			var templResult = $('#flowerTmpl').tmpl(data);
			templResult.slice(0, 3).appendTo('#row1');
			templResult.slice(3).appendTo("#row2");

			$('form').validate({
				highlight: function (element, errorClass) {
					$(element).add($(element).parent()).addClass("invalidElem");
				},
				unhighlight: function (element, errorClass) {
					$(element).add($(element).parent()).removeClass("invalidElem");
				},
				errorElement: "div",
				errorClass: "errorMsg"
			});

			$.validator.addClassRules({
				flowerValidation: {
					min: 0
				}
			})

			$('input').addClass("flowerValidation").change(function (e) {
				$('form').validate().element($(e.target));
			});
		});
	</script>
	<script id="flowerTmpl" type="text/x-jquery-tmpl">
		<div class="dcell">
			<img src="${product}.png" />
			<label for="${product}">${name}: </label>
			<input name="${product}" value="0" required />
		</div>
	</script>
</head>
<body>
	<h1>Jacqui's Flower Shop</h1>
	<form method="post" action="http://node.jacquisflowershop.com/order">
		<div id="oblock">
			<div class="dtable">
				<div id="row1" class="drow">
				</div>
				<div id="row2" class="drow">
				</div>
			</div>
		</div>
		<div id="buttonDiv">
			<button type="submit">Place Order</button></div>
	</form>
</body>
</html>

Примечание

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

Импортирование файла JavaScript

Первая вещь, которую нам нужно сделать, это вставить плагин валидации в документ, вот таким вот образом:

<script src="jquery.validate.js" type="text/javascript"></script>

Я использовал отладочную версию файла, но доступна также и минимизированная версия и некоторые, но не все, сервисы CDN, потому что этот файл очень популярен.

Конфигурация валидации

Следующий шаг – это конфигурация валидации элемента form, которую вы делаете, вызвав метод validate для элементов form, для которых вы хотите провести валидацию. Аргументом метода validate является объект-карта, который содержит настройки конфигурации, как показано в листинге 13-8.

Листинг 13-8: Конфигурация валидации
$('form').validate({
	highlight: function (element, errorClass) {
		$(element).add($(element).parent()).addClass("invalidElem");
	},
	unhighlight: function (element, errorClass) {
		$(element).add($(element).parent()).removeClass("invalidElem");
	},
	errorElement: "div",
	errorClass: "errorMsg"
});

Я указал значения для четырех опций (highlight, unhighlight, errorElement и errorClass); позже я вернусь и объясню их значения.

Определение правил валидации

Гибкость плагина валидации во многом обусловлена способом, которым вы быстро и просто можете указать правила для тестирования правильности ввода данных. Есть много различных путей связывания правил с элементами. Один, которым я стараюсь пользоваться, работает через классы. Вы определяете набор правил и связываете его с классом, и когда форма валидируется, правила применяются ко всем элементам form, которые являются членами указанного класса. Я создал только одно правило в примере, как показано в листинге 13-9.

Листинг 13-9: Определение правил валидации
$.validator.addClassRules({
	flowerValidation: {
		min: 0
	}
})

В данном случае я создал новое правило, которое будет применяться ко всем элементам form, которые являются членами класса flowerValidation. Правило заключается в том, что значение должно быть равно или больше 0. Я выразил в правиле условие, используя min. Это только один из множества удобных стандартных правил валидации, которые предоставляет плагин валидации, и я опишу все из них позже в этой главе.

Применение правил валидации

Вы свяжете правила валидации с элементами в форме, если добавите элементу класс, который вы указали на предыдущем этапе. Это дает вам возможность "подогнать" валидацию для различных видов элементов в форме. Для этого примера все элементы будут обрабатываться одинаково, поэтому я использую jQuery, чтобы выбрать все элементы ввода данных и добавить класс flowerValidation, как показано в листинге 13-10.

Листинг 13-10: Добавление элементов input классу, связанному с валидацией
$('input').addClass("flowerValidation").change(function (e) {
	$('form').validate().element($(e.target));
});

Я также использовал функцию, связанную с событием change, чтобы напрямую валидировать элемент, чье значение поменялось. Это гарантирует, что пользователь получит немедленный ответ, если исправит ошибку. Результат работы плагина валидации можно увидеть на рисунке 13-5. Чтобы получить этот рисунок, я ввел -1 в поле ввода и нажал кнопку Place Order.

Рисунок 13-5: Использование плагина валидации

Совет

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

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

Использование правил валидации

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

Таблица 13-3: Правила плагина Validation
Правила Описание
creditcard: true Значение должно содержать номер кредитной карты
date: true Значение должно быть текущей JavaScript датой
digits: true Значение должно содержать только цифры
email: true Значение должно быть действительным e-mail адресом
max: maxVal Значение должно быть не больше чем maxVal
maxlength: length Значение должно содержать символов не более, чем length
min: minVal Значение должно быть не меньше чем minVal
minlength: length Значение должно содержать символов не менее, чем length
number: true Значение должно быть десятичным числом
range: [minVal, maxVal] Значение должно находиться в пределах minVal и maxVal
rangelength: [minLen, maxLen] Значение должно содержать символом минимум minLen и не более, чем maxLen
required: true; Требуемое значение
url: true Значение должно быть URL

Вы можете объединить несколько правил в одно. Это позволит вам провести комплексную валидацию быстро и качественно.

Совет

В дистрибутив плагина валидации входит файл с названием additional-methods.js. Этот файл определяет некоторые дополнительные правила, включая номера телефонов США и Великобритании, адреса IPv4 и IPv6, некоторые дополнительные форматы данных, e-mail и URL.

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

Примечание

Плагин Validation также поддерживает удаленную валидацию, когда данные, введенные пользователем, проверяются через удаленный сервер. Это полезно, когда данные не могут быть распределены на клиентскую сторону, потому что это может быть небезопасно или непрактично (например, проверка на то, что имя пользователя не было до сих пор использовано). Я покажу удаленную валидацию в главе 16, после того, как мы изучим возможности, которые она использует, в главах 14 и 15.

Применение правил валидации через классы

Как оказалось, наиболее часто я провожу валидацию через классы. Этот подход я применяю и в примере. Кстати, вы не ограничены одним правилом. Можно применять несколько правил вместе, чтобы провести валидацию различных аспектов значения, которое предоставляет пользователь, как показано в листинге 13-11.

Листинг 13-11: Объединение нескольких правил в одно
<script type="text/javascript">
	$(document).ready(function () {
		var data = [
			{ name: "Astor", product: "astor", stocklevel: "10", price: "2.99" },
			{ name: "Daffodil", product: "daffodil", stocklevel: "12", price: "1.99" },
			{ name: "Rose", product: "rose", stocklevel: "2", price: "4.99" },
			{ name: "Peony", product: "peony", stocklevel: "0", price: "1.50" },
			{ name: "Primula", product: "primula", stocklevel: "1", price: "3.12" },
			{ name: "Snowdrop", product: "snowdrop", stocklevel: "15", price: "0.99" },
		];

		var templResult = $('#flowerTmpl').tmpl(data);
		templResult.slice(0, 3).appendTo('#row1');
		templResult.slice(3).appendTo("#row2");

		$('form').validate({
			highlight: function (element, errorClass) {
				$(element).add($(element).parent()).addClass("invalidElem");
			},
			unhighlight: function (element, errorClass) {
				$(element).add($(element).parent()).removeClass("invalidElem");
			},
			errorElement: "div",
			errorClass: "errorMsg"
		});

		$.validator.addClassRules({
			flowerValidation: {
				required: true,
				digits: true,
				min: 0,
				max: 100
			}
		})

		$('input').addClass("flowerValidation").change(function (e) {
			$('form').validate().element($(e.target));
		});
	});
</script>

В этом примере я объединил правила required, digits, min и max, чтобы убедиться, что пользователь добавит значение, которое содержит только цифры, расположенные в диапазоне от 0 до 100.

Обратите внимание, что я связываю правило с классом, используя метод addClassRules. Аргументами этого метода являются один или несколько наборов правил и имя класса, к которому они будут применяться. Как показано в примере, вы вызываете метод addClassRules для свойства validator главной jQuery $ функции.

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

Рисунок 13-6: Применение нескольких правил валидации для элементов form

Я ввел различные значения, которые "провалят" одно из правил. Важно отметить, что правила выполняются в том порядке, в котором вы их объединили в одном правиле. Если вы посмотрите на сообщение об ошибке для товара Rose, вы заметите, что он провалился на проверке digits. Если вы поменяете порядок правил, вы можете получить другую ошибку. В листинге 13-12 показан измененный порядок правил.

Листинг 13-12: Изменение порядка применения правил
$.validator.addClassRules({
	flowerValidation: {
		required: true,
		min: 0,
		max: 100,
		digits: true
	}
})

В этом примере я переместил проверку digits в конец правила. Если я введу в поле формы -1, тогда провалится проверка min, как показано на рисунке 13-7.

Рисунок 13-7: Изменения порядка применения правил во время проведения валидации

Применение правил валидации напрямую к элементам

Следующая технология позволяет применять правила валидации к отдельным элементам, как показано в листинге 13-13.

Листинг 13-13: Применение правил валидации к элементам в выборке
<script type="text/javascript">
	$(document).ready(function () {
		var data = [
			{ name: "Astor", product: "astor", stocklevel: "10", price: "2.99" },
			{ name: "Daffodil", product: "daffodil", stocklevel: "12", price: "1.99" },
			{ name: "Rose", product: "rose", stocklevel: "2", price: "4.99" },
			{ name: "Peony", product: "peony", stocklevel: "0", price: "1.50" },
			{ name: "Primula", product: "primula", stocklevel: "1", price: "3.12" },
			{ name: "Snowdrop", product: "snowdrop", stocklevel: "15", price: "0.99" },
		];

		var templResult = $('#flowerTmpl').tmpl(data);
		templResult.slice(0, 3).appendTo('#row1');
		templResult.slice(3).appendTo("#row2");

		$('form').validate({
			highlight: function (element, errorClass) {
				$(element).add($(element).parent()).addClass("invalidElem");
			},
			unhighlight: function (element, errorClass) {
				$(element).add($(element).parent()).removeClass("invalidElem");
			},
			errorElement: "div",
			errorClass: "errorMsg"
		});

		$.validator.addClassRules({
			flowerValidation: {
				required: true,
				min: 0,
				max: 100,
				digits: true,
			}
		})

		$('#row1 input').each(function (index, elem) {
			$(elem).rules("add", {
				min: 10,
				max: 20
			})
		});

		$('input').addClass("flowerValidation").change(function (e) {
			$('form').validate().element($(e.target));
		});
	});
</script>

Обратите внимание, что вы вызываете метод правил для объекта jQuery, передавая ему строку add и объект-карту с правилами проверки, которые вы хотите применить, и их аргументами. Метод rules работает только с первым элементом в выборке, поэтому нужно использовать метод each, если вы хотите более широко применить правила. В данном примере я выбрал все элементы input, которые являются потомками элемента row1, и применил к ним набор правил.

Совет

Можно удалить правила для элемента, если заменить add на remove при вызове метода rules.

Правила, которые применяются к элементам с использованием метода rules, оцениваются раньше правил, применяемых с использованием класса. Для моего примера это обозначает, что элементы input в верхнем ряду будут проверяться с использованием значения min равном 10 и значением max равном 20, в то время как другие элементы input будут, соответственно, использовать значения от 0 до 100. Результат можно увидеть на рисунке 13-8.

Рисунок 13-8: Применение правил напрямую к элементам

Поскольку вы работаете с каждым элементом в отдельности, вы можете подгонять проверку под свои нужды. В листинге 13-14 показан пример.

Листинг 13-14: Корректировка правил для элементов
<script type="text/javascript">
	$(document).ready(function () {
		var data = [
			{ name: "Astor", product: "astor", stocklevel: "10", price: "2.99" },
			{ name: "Daffodil", product: "daffodil", stocklevel: "12", price: "1.99" },
			{ name: "Rose", product: "rose", stocklevel: "2", price: "4.99" },
			{ name: "Peony", product: "peony", stocklevel: "0", price: "1.50" },
			{ name: "Primula", product: "primula", stocklevel: "1", price: "3.12" },
			{ name: "Snowdrop", product: "snowdrop", stocklevel: "15", price: "0.99" },
		];

		var templResult = $('#flowerTmpl').tmpl(data);
		templResult.slice(0, 3).appendTo('#row1');
		templResult.slice(3).appendTo("#row2");

		$('form').validate({
			highlight: function (element, errorClass) {
				$(element).add($(element).parent()).addClass("invalidElem");
			},
			unhighlight: function (element, errorClass) {
				$(element).add($(element).parent()).removeClass("invalidElem");
			},
			errorElement: "div",
			errorClass: "errorMsg"
		});

		$('input').each(function (index, elem) {
			var rules = {
				required: true,
				min: 0,
				max: data[index].stocklevel,
				digits: true
			}
			if (Number(data[index].price) > 3.00) {
				rules.max--;
			}
			$(elem).rules("add", rules);
		});

		$('input').change(function (e) {
			$('form').validate().element($(e.target));
		});
	});
</script>

В этом примере я подогнал значение правила max, используя объект данных, который я добавил в документ, чтобы сгенерировать элементы с использованием шаблона. Значение правила max устанавливается с использованием свойства stocklevel и корректируется вниз, если цена больше $3. Если у вас есть данные наподобие этих, вы можете провести гораздо более полезную валидацию. Результат этого изменения можно увидеть на рисунке 13-9.

Рисунок 13-9: Установление различных значений для правил валидации на основании данных

Применение правил валидации через атрибут элемента name

Можно применить правила валидации к элементам, основываясь на значении атрибута name. Спецификация HTML не требует, чтобы значение атрибута name было уникальным, и одно значение часто используется для категоризации группы элементов form. В моем примере документа цветочного магазина все имена разные, и каждое из них соответствует отдельному продукту. В любом случае, вы можете создать правила, которые соответствуют значению атрибута name, и правила, которые применяются ко всем элементам с этим значением. В листинге 13-15 показан пример.

Листинг 13-15: Применение правил валидации на основании имени элемента
<script type="text/javascript">
	$(document).ready(function () {
		var data = [
			{ name: "Astor", product: "astor", stocklevel: "10", price: "2.99" },
			{ name: "Daffodil", product: "daffodil", stocklevel: "12", price: "1.99" },
			{ name: "Rose", product: "rose", stocklevel: "2", price: "4.99" },
			{ name: "Peony", product: "peony", stocklevel: "0", price: "1.50" },
			{ name: "Primula", product: "primula", stocklevel: "1", price: "3.12" },
			{ name: "Snowdrop", product: "snowdrop", stocklevel: "15", price: "0.99" },
		];

		var templResult = $('#flowerTmpl').tmpl(data);
		templResult.slice(0, 3).appendTo('#row1');
		templResult.slice(3).appendTo("#row2");

		var rulesList = new Object();
		for (var i = 0; i < data.length; i++) {
			rulesList[data[i].product] = {
				min: 0,
				max: data[i].stocklevel,
			}
		}

		$('form').validate({
			highlight: function (element, errorClass) {
				$(element).add($(element).parent()).addClass("invalidElem");
			},
			unhighlight: function (element, errorClass) {
				$(element).add($(element).parent()).removeClass("invalidElem");
			},
			errorElement: "div",
			errorClass: "errorMsg",
			rules: rulesList
		});

		$('input').change(function (e) {
			$('form').validate().element($(e.target));
		});
	});
</script>

Вы добавляете правила, которые основываются на именах элементов, используя свойство rules объекта конфигурации, который вы передаете методу validate, когда проводите валидацию для form. Обратите внимание, что я просто использовал объект данных, чтобы создать набор правил (также обратите внимание, что свойство product в объекте данных используется для генерирования атрибута name в элементах input). Я стараюсь не использовать такой подход, поскольку мне больше нравится работать напрямую с элементами в документе; но это технология может пригодиться, если у вас есть объект данных и вы хотите провести валидацию, прежде чем элементы формы будут добавлены в документ.

Применение правил валидации с использованием атрибутов элементов

И наконец, вы можете применять правила валидации, используя атрибуты элементов. Плагин валидации проверяет элементы form, чтобы увидеть, определяют ли они атрибуты, которые соответствуют имени встроенных правил проверки; таким образом считается, что для элемента, который определяет атрибут required, требуется обязательная проверка. В листинге 13-16 показан пример.

Листинг 13-16: Приведение валидации с использованием атрибута элемента
<script type="text/javascript">
	$(document).ready(function () {
		var data = [
			{ name: "Astor", product: "astor", stocklevel: "10", price: "2.99" },
			{ name: "Daffodil", product: "daffodil", stocklevel: "12", price: "1.99" },
			{ name: "Rose", product: "rose", stocklevel: "2", price: "4.99" },
			{ name: "Peony", product: "peony", stocklevel: "0", price: "1.50" },
			{ name: "Primula", product: "primula", stocklevel: "1", price: "3.12" },
			{ name: "Snowdrop", product: "snowdrop", stocklevel: "15", price: "0.99" },
		];

		var templResult = $('#flowerTmpl').tmpl(data);
		templResult.slice(0, 3).appendTo('#row1');
		templResult.slice(3).appendTo("#row2");

		$('form').validate({
			highlight: function (element, errorClass) {
				$(element).add($(element).parent()).addClass("invalidElem");
			},
			unhighlight: function (element, errorClass) {
				$(element).add($(element).parent()).removeClass("invalidElem");
			},
			errorElement: "div",
			errorClass: "errorMsg",
		});

		$('input').change(function (e) {
			$('form').validate().element($(e.target));
		});
	});
</script>
<script id="flowerTmpl" type="text/x-jquery-tmpl">
	<div class="dcell">
		<img src="${product}.png" />
		<label for="${product}">${name}: </label>
		<input name="${product}" value="0" required min="0" max="${stocklevel}" />
	</div>
</script>

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

Указание сообщений валидации

Плагин валидации определяет сообщения об ошибке по умолчанию для всех встроенных правил, но они довольно общие и не всегда полезны пользователю. Вот простой пример: если вы укажете для проверки max значение 10, а пользователь введет в поле 20, тогда сообщение об ошибке будет следующим:

Please enter a value less than or equal to 10

Это сообщение описывает ограничение, которое вы применили к элементу, но оно не дает никаких разъяснений пользователю, почему тут вообще есть лимит. К счастью, вы можете менять эти сообщения, чтобы добавить дополнительный контекст и скорректировать их для своих нужд. Метод, который вы используете для изменения сообщений, зависит, в первую очередь, от того, как вы создавали правила валидации. Вы не можете менять сообщения, если вы применяли правила, используя класс, но в следующих разделах я расскажу, как менять сообщения, еcли были использованы другие технологии.

Указание сообщений для атрибутивной и именной валидации

Если вы используете атрибут name или проверочные атрибуты, чтобы связать правила с элементами, вы можете изменить сообщения, если добавите свойство messages в объект options, который вы передаете методу validate, когда настраиваете валидацию. В листинге 13-17 показан пример.

Листинг 13-17: Использование свойства messages объекта оbject
<script type="text/javascript">
	$(document).ready(function () {
		var data = [
			{ name: "Astor", product: "astor", stocklevel: "10", price: "2.99" },
			{ name: "Daffodil", product: "daffodil", stocklevel: "12", price: "1.99" },
			{ name: "Rose", product: "rose", stocklevel: "2", price: "4.99" },
			{ name: "Peony", product: "peony", stocklevel: "0", price: "1.50" },
			{ name: "Primula", product: "primula", stocklevel: "1", price: "3.12" },
			{ name: "Snowdrop", product: "snowdrop", stocklevel: "15", price: "0.99" },
		];

		var templResult = $('#flowerTmpl').tmpl(data);
		templResult.slice(0, 3).appendTo('#row1');
		templResult.slice(3).appendTo("#row2");

		$('form').validate({
			highlight: function (element, errorClass) {
				$(element).add($(element).parent()).addClass("invalidElem");
			},
			unhighlight: function (element, errorClass) {
				$(element).add($(element).parent()).removeClass("invalidElem");
			},
			errorElement: "div",
			errorClass: "errorMsg",
			messages: {
				rose: {
					max: "We don't have that many roses in stock!"
				},
				peony: {
					max: "We don't have that many peonies in stock!"
				}
			}
		});

		$('input').change(function (e) {
			$('form').validate().element($(e.target));
		});
	});
</script>

Вы можете рассматривать структуру объекта, который вы предоставляете, как значение свойства messages. Вы указываете свойство, используя имя элемента, в котором вы заинтересованы, и указываете, чтобы значение этого свойства было объектом-картой между проверкой и новым сообщением, которое вы хотите использовать. В данном примере я изменил сообщение для правила max для элементов с именами rose и peony. Результат можно увидеть на рисунке 13-10. Оба эти элемента показаны со скорректированным сообщением валидации.

Рисунок 13-10: Изменение сообщение через объект options

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

Листинг 13-18: Определение пользовательских сообщений программно
<script type="text/javascript">
	$(document).ready(function () {
		var data = [
			{ name: "Astor", product: "astor", stocklevel: "10", price: "2.99" },
			{ name: "Daffodil", product: "daffodil", stocklevel: "12", price: "1.99" },
			{ name: "Rose", product: "rose", stocklevel: "2", price: "4.99" },
			{ name: "Peony", product: "peony", stocklevel: "0", price: "1.50" },
			{ name: "Primula", product: "primula", stocklevel: "1", price: "3.12" },
			{ name: "Snowdrop", product: "snowdrop", stocklevel: "15", price: "0.99" },
		];

		var templResult = $('#flowerTmpl').tmpl(data);
		templResult.slice(0, 3).appendTo('#row1');
		templResult.slice(3).appendTo("#row2");

		var customMessages = new Object();
		for (var i = 0; i < data.length; i++) {
			customMessages[data[i].product] = {
				max: "We only have " + data[i].stocklevel + " in stock"
			}
		}

		$('form').validate({
			highlight: function (element, errorClass) {
				$(element).add($(element).parent()).addClass("invalidElem");
			},
			unhighlight: function (element, errorClass) {
				$(element).add($(element).parent()).removeClass("invalidElem");
			},
			errorElement: "div",
			errorClass: "errorMsg",
			messages: customMessages
		});

		$('input').change(function (e) {
			$('form').validate().element($(e.target));
		});
	});
</script>

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

Изменение сообщений для валидации каждого элемента

Когда вы применяете правила к отдельным элементам, вы можете использовать объект messages, который определит желаемые сообщения. В листинге 13-19 показано, как это делается.

Листинг 13-19: Указание сообщений для правил, применяемых к каждому элементу
<script type="text/javascript">
	$(document).ready(function () {
		var data = [
			{ name: "Astor", product: "astor", stocklevel: "10", price: "2.99" },
			{ name: "Daffodil", product: "daffodil", stocklevel: "12", price: "1.99" },
			{ name: "Rose", product: "rose", stocklevel: "2", price: "4.99" },
			{ name: "Peony", product: "peony", stocklevel: "0", price: "1.50" },
			{ name: "Primula", product: "primula", stocklevel: "1", price: "3.12" },
			{ name: "Snowdrop", product: "snowdrop", stocklevel: "15", price: "0.99" },
		];

		var templResult = $('#flowerTmpl').tmpl(data);
		templResult.slice(0, 3).appendTo('#row1');
		templResult.slice(3).appendTo("#row2");

		$('form').validate({
			highlight: function (element, errorClass) {
				$(element).add($(element).parent()).addClass("invalidElem");
			},
			unhighlight: function (element, errorClass) {
				$(element).add($(element).parent()).removeClass("invalidElem");
			},
			errorElement: "div",
			errorClass: "errorMsg",
		});

		$('input').each(function (index, elem) {
			$(elem).rules("add", {
				min: 10,
				max: 20,
				messages: {
					max: "You only have " + data[index].stocklevel + " in stock"
				}
			})
		}).change(function (e) {
			$('form').validate().element($(e.target));
		});
	});
</script>

Еще раз, я использовал свойство stockvalue для указания сообщения. Для простоты я взял за предположение, что элементы input расположены в том же порядке, что и единицы данных. Результат использования этого сообщения можно увидеть на рисунке 13-11.

Рисунок 13-11: Указание сообщений, полученных из объекта данных

Создание пользовательской проверки

Можно создать пользовательские правила валидации, если встроенные не соответствует нашим требованиям. Это довольно простой процесс, что обозначает, что вы можете тесно связать валидацию со структурой и природой вашего веб приложения. В листинге 13-20 представлен пример.

Листинг 13-20: Создание пользовательской валидации
<script type="text/javascript">
	$(document).ready(function () {

		var data = [
			{ name: "Astor", product: "astor", stocklevel: "10", price: "2.99" },
			{ name: "Daffodil", product: "daffodil", stocklevel: "12", price: "1.99" },
			{ name: "Rose", product: "rose", stocklevel: "2", price: "4.99" },
			{ name: "Peony", product: "peony", stocklevel: "0", price: "1.50" },
			{ name: "Primula", product: "primula", stocklevel: "1", price: "3.12" },
			{ name: "Snowdrop", product: "snowdrop", stocklevel: "15", price: "0.99" },
		];

		var templResult = $('#flowerTmpl').tmpl(data);
		templResult.slice(0, 3).appendTo('#row1');
		templResult.slice(3).appendTo("#row2");

		$('form').validate({
			highlight: function (element, errorClass) {
				$(element).add($(element).parent()).addClass("invalidElem");
			},
			unhighlight: function (element, errorClass) {
				$(element).add($(element).parent()).removeClass("invalidElem");
			},
			errorElement: "div",
			errorClass: "errorMsg",
		});

		$.validator.addMethod("stock", function (value, elem, args) {
			return Number(value) < Number(args);
		}, "We don't have that many in stock");

		$('input').each(function (index, elem) {
			$(elem).rules("add", {
				min: 0,
				stock: data[index].stocklevel
			})
		}).change(function (e) {
			$('form').validate().element($(e.target));
		});
	});
</script>

Вы указываете пользовательское правило, используя метод addMethod, который вызывается для свойства validator функции $. Аргументами этого метода являются имя, которым вы хотите обозначить правило, функция, которая используется для проведения валидации, и сообщение, которое будет показано, если валидация не прошла. В этом примере я определил правило с именем stock.

Определение функции валидации

Аргументами пользовательской функции валидации являются значение, введенное пользователем, объект HTMLElement, представляющий элемент формы, и любые аргументы, которые были указаны, когда правило применяется к элементу для валидации, например, вот так:

$(elem).rules("add", {
	min: 0,
	stock: data[index].stocklevel
})

Когда я применил правило, я указал значение свойства stocklevel в качестве аргумента для проверки. И это передано пользовательской функции валидации.

function(value, elem, args) {
	return Number(value) <= Number(args);
}

Вы определяете, валидно ли значение, по результату, возвращенному функцией. Если значение валидно, возвращается true. Если нет, то возвращается false. Значение и аргументы представлены как строка, что обозначает, что мне нужно использовать тип Number, чтобы убедиться, что JavaScript сравнивает значения как числа. Для моей функции значение валидно, если оно меньше или равно аргументу.

Определение сообщения о валидации

Указать сообщение, которое будет отображено, можно двумя способами. Первым из них является использование строки, это тот способ, который я представил в предыдущих примерах. Второй способ указания сообщения – это использование функции, что позволяет создавать сообщения с гораздо большим количеством контекста. В листинге 13-21 представлен пример.

Листинг 13-21: Создание сообщения для пользовательской проверки с использованием функции
<script type="text/javascript">
	$(document).ready(function () {
		var data = [
			{ name: "Astor", product: "astor", stocklevel: "10", price: "2.99" },
			{ name: "Daffodil", product: "daffodil", stocklevel: "12", price: "1.99" },
			{ name: "Rose", product: "rose", stocklevel: "2", price: "4.99" },
			{ name: "Peony", product: "peony", stocklevel: "0", price: "1.50" },
			{ name: "Primula", product: "primula", stocklevel: "1", price: "3.12" },
			{ name: "Snowdrop", product: "snowdrop", stocklevel: "15", price: "0.99" },
		];

		var templResult = $('#flowerTmpl').tmpl(data);
		templResult.slice(0, 3).appendTo('#row1');
		templResult.slice(3).appendTo("#row2");

		$('form').validate({
			highlight: function (element, errorClass) {
				$(element).add($(element).parent()).addClass("invalidElem");
			},
			unhighlight: function (element, errorClass) {
				$(element).add($(element).parent()).removeClass("invalidElem");
			},
			errorElement: "div",
			errorClass: "errorMsg",
		});

		$.validator.addMethod("stock", function (value, elem, args) {
			return Number(value) <= Number(args);
		}, function (args) {
			return "We only have " + args + " in stock"
		});

		$('input').each(function (index, elem) {
			$(elem).rules("add", {
				min: 0,
				stock: data[index].stocklevel
			})
		}).change(function (e) {
			$('form').validate().element($(e.target));
		});
	});
</script>

Аргумент функции – это аргумент, который вы предоставляете, когда применяете правило. Для этого примера это значение свойства stocklevel. Результат можно увидеть на рисунке 13-12.

Рисунок 13-12: Указание сообщения об ошибке для пользовательской проверки с использованием функции

Форматирование вывода на экран ошибки валидации

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

Листинг 13-22: Опции конфигурации для форматирования ошибок валидации
<script type="text/javascript">
	$(document).ready(function () {
		var data = [
			{ name: "Astor", product: "astor", stocklevel: "10", price: "2.99" },
			{ name: "Daffodil", product: "daffodil", stocklevel: "12", price: "1.99" },
			{ name: "Rose", product: "rose", stocklevel: "2", price: "4.99" },
			{ name: "Peony", product: "peony", stocklevel: "0", price: "1.50" },
			{ name: "Primula", product: "primula", stocklevel: "1", price: "3.12" },
			{ name: "Snowdrop", product: "snowdrop", stocklevel: "15", price: "0.99" },
		];

		var templResult = $('#flowerTmpl').tmpl(data);
		templResult.slice(0, 3).appendTo('#row1');
		templResult.slice(3).appendTo("#row2");

		$('form').validate({
			highlight: function (element, errorClass) {
				$(element).add($(element).parent()).addClass("invalidElem");
			},
			unhighlight: function (element, errorClass) {
				$(element).add($(element).parent()).removeClass("invalidElem");
			},
			errorElement: "div",
			errorClass: "errorMsg",
		});

		$.validator.addMethod("stock", function (value, elem, args) {
			return Number(value) <= Number(args);
		}, function (args) {
			return "We only have " + args + " in stock"
		});

		$('input').each(function (index, elem) {
			$(elem).rules("add", {
				min: 0,
				stock: data[index].stocklevel
			})
		}).change(function (e) {
			$('form').validate().element($(e.target));
		});
	});
</script>

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

Создание класса для недействительных элементов

Опция errorClass позволяет создать класс, который будет связан с недействительными значениями. Этот класс применяется к сообщениям об ошибках, когда они добавляются в документ. В моем примере я указываю класс с именем errorMsg, для которого есть соответствующий селектор в элементе style, как показано в листинге 13-23.

Листинг 13-23: Элемент style для примера документа
<style type="text/css">
	div.errorMsg { color: red; }
	.invalidElem { border: medium solid red; }
</style>

Я просто использовал свойство color, так что текст валидации показан красным цветом.

Создание элемента сообщения об ошибке

Сообщение об ошибке вставляется в документ в качестве ближайшего следующего сиблинга элемента формы, который содержит недействительное значение. По умолчанию текст сообщения об ошибке содержится внутри элемента label. Мне это не подходит в данных примерах, потому что внешняя таблица стилей содержит селектор, который подходит ко всем элементам label внутри элементов div каждой ячейки в табличном стиле CSS, и применяет стиль, который мешает тексту корректно отображаться. Чтобы исправить это, я использовал опцию errorElement, чтоб указать, что для этого должен быть использован элемент div, как показано в листинге 13-24.

Листинг 13-24: Указание элемента, который будет использоваться для сообщений об ошибках
$('form').validate({
	highlight: function (element, errorClass) {
		$(element).add($(element).parent()).addClass("invalidElem");
	},
	unhighlight: function (element, errorClass) {
		$(element).add($(element).parent()).removeClass("invalidElem");
	},
	errorElement: "div",
	errorClass: "errorMsg",
});

Создание выделения для недействительных элементов

Опции highlight и unhighlight позволяют вам указать функции, которые используются для подчеркивания элементов, содержащих некорректные значения. Аргументами функции являются объект HTMLElement, представляющий недействительный элемент, и класс, который вы указали, используя опцию errorClass. Как вы видите по выделенным выражениям в листинге 13-25, я проигнорировал второй атрибут, но использовал объект HTMLElement, чтобы создать выборку jQuery, перейти к родительскому элементу и добавить его к классу invalidElem.

Листинг 13-25: Выделение элемента
$('form').validate({
	highlight: function (element, errorClass) {
		$(element).add($(element).parent()).addClass("invalidElem");
	},
	unhighlight: function (element, errorClass) {
		$(element).add($(element).parent()).removeClass("invalidElem");
	},
	errorElement: "div",
	errorClass: "errorMsg",
});

Функция, указанная опцией unhighlight, вызывается, когда пользователь исправил ошибку и элемент содержит корректное значение. Я использую эту возможность, чтобы удалить класс, добавленный другой функцией. Класс invalidElem соответствует селектору в элементе style, содержащемся в документе, как показано в листинге 13-26.

Листинг 13-26: Стиль, используемый для выделения элементов
<style type="text/css">
	div.errorMsg {color: red}
	.invalidElem {border: medium solid red}
</style>

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

Использование сводки результата валидации

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

Листинг 13-27: Использование сводки результата валидации
<script type="text/javascript">
	$(document).ready(function () {
		var data = [
			{ name: "Astor", product: "astor", stocklevel: "10", price: "2.99" },
			{ name: "Daffodil", product: "daffodil", stocklevel: "12", price: "1.99" },
			{ name: "Rose", product: "rose", stocklevel: "2", price: "4.99" },
			{ name: "Peony", product: "peony", stocklevel: "0", price: "1.50" },
			{ name: "Primula", product: "primula", stocklevel: "1", price: "3.12" },
			{ name: "Snowdrop", product: "snowdrop", stocklevel: "15", price: "0.99" },
		];

		var plurals = {
			astor: "Astors",
			daffodil: "Daffodils",
			rose: "Roses",
			peony: "Peonies",
			primula: "Primulas",
			snowdrop: "Snowdrops"
		}

		var templResult = $('#flowerTmpl').tmpl(data);
		templResult.slice(0, 3).appendTo('#row1');
		templResult.slice(3).appendTo("#row2");

		$('<div id=errorSummary>Please correct the following errors:</div>')
			.append('<ul id="errorsList"></ul>').hide().insertAfter('h1');

		$('form').validate({
			highlight: function (element, errorClass) {
				$(element).addClass("invalidElem");
			},
			unhighlight: function (element, errorClass) {
				$(element).removeClass("invalidElem");
			},
			errorContainer: '#errorSummary',
			errorLabelContainer: '#errorsList',
			wrapper: 'li',
			errorElement: "div"
		});

		$.validator.addMethod("stock", function (value, elem, args) {
			return Number(value) <= Number(args.data.stocklevel);
		}, function (args) {
			return "You requested " + $(args.element).val() + " "
				+ plurals[args.data.product] + " but you only have "
				+ args.data.stocklevel + " in stock";
		});

		$('input').each(function (index, elem) {
			$(elem).rules("add", {
				min: 0,
				stock: {
					index: index,
					data: data[index],
					element: elem
				}
			})
		}).change(function (e) {
			$('form').validate().element($(e.target));
		});
	});
</script>

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

Рисунок 13-13: Использование сводки результата валидации

Подготовка сообщения о валидации

Первая задача, которую нужно решить при использовании сводки валидации, – это то, что содержание, которое предположительно должно быть размещено рядом с элементом теряется; вы должны дополнительно проработать сообщения об ошибках, чтобы они имели смысл. Для начала я определил объект, который содержит множественное число для названий цветов:

var plurals = {
	astor: "Astors",
	daffodil: "Daffodils",
	rose: "Roses",
	peony: "Peonies",
	primula: "Primulas",
	snowdrop: "Snowdrops"
}

Я использую эти значения, чтобы сгенерировать указанное сообщение об ошибке, полагаясь на возможности функции пользовательской проверки, вот таким образом:

$.validator.addMethod("stock", function (value, elem, args) {
	return Number(value) <= Number(args.data.stocklevel);
}, function (args) {
	return "You requested " + $(args.element).val() + " "
		+ plurals[args.data.product] + " but you only have "
		+ args.data.stocklevel + " in stock";
});

Связью между двумя этими этапами является объект аргумента, который я указал, когда применил пользовательскую проверку к элементам формы. У встроенных правил простые аргументы, но вы можете создавать сложные объекты и добавлять данные, которые вам подходят:

$('input').each(function (index, elem) {
	$(elem).rules("add", {
		min: 0,
		stock: {
			index: index,
			data: data[index],
			element: elem
		}
	})
}).change(function (e) {
	$('form').validate().element($(e.target));
});

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

Создание сводки валидации

Вы несете ответственность за создание элемента, который будет содержать сводку валидации, и за добавление его в документ. Я добавил элемент div, который содержит элемент ul. Моей целью является создание ненумерованного списка, показывающего каждую ошибку:

$('<div id=errorSummary>Please correct the following errors:</div>')
	.append('<ul id="errorsList"></ul>').hide().insertAfter('h1');

Я включил немного текста в элемент div. Он будет отображаться над списком ошибок. Я выделил вызов метода hide. Убедитесь, чтобы элемент не было видно. Это не обязательно. Элемент может быть видим все время, но я считаю хорошей практикой показывать сводку валидации, только если есть ошибки, которые должен исправить пользователь.

Теперь, когда у нас все части на месте, мы можем вернуться к опциям, которые мы будем использовать для конфигурации сводки валидации:

$('form').validate({
	highlight: function (element, errorClass) {
		$(element).addClass("invalidElem");
	},
	unhighlight: function (element, errorClass) {
		$(element).removeClass("invalidElem");
	},
	errorContainer: '#errorSummary',
	errorLabelContainer: '#errorsList',
	wrapper: 'li',
	errorElement: "div"
});

Я ужесточил фокус функций hightlight и unhighlight, так что выделен только элемент input, но важные опции тоже выделены.

Опция errorContainer указывает элемент, который будет становиться видимым, когда есть ошибки валидации, которые должны быть отображены. В моем случае это элемент с ID errorSummary (элемент div).

Опция errorLabelContainer указывает элемент, в который будут вставляться отдельные сообщения об ошибках. В моем случае это элемент ul, поскольку я хочу, чтобы эти сообщения отображались как список.

Опция wrapper указывает элемент, в который будет вставлено сообщение о валидации. Это действительно полезно только тогда, когда вы хотите изображение в виде списка. И наконец, errorElement указывает элемент, который будет содержать текст ошибки. По умолчанию это элемент label, но я переключился на элементы div, чтобы облегчить форматирование. Результатом этих действий является сводка валидации, которая показана на рисунке 13-13.

Плагин валидации удаляет сообщения, если пользователь исправил ошибку, и если ошибок вообще нет, то пользователь может отправить форму. На рисунке 13-14 показана сводка валидации, после того, как были исправлены две ошибки, имевшие место на последнем рисунке.

Рисунок 13-14: Сводка валидации с меньшим числом ошибок

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