CoffeeScript в примерах. Часть 1: Валидация

от автора

В статье приведено решение и описано его использование. Заказывайте темы для следующих статей в ЛС.

Тут можно получить информацию

  • примеры использования CoffeeScript
  • “классовое” ООП с CoffeeScript
  • примеры удачных решений структуры алгоритма
  • jQuery вместе с CoffeeScript
  • микрошаблонизатор на CoffeeScript


Ниже доступны:

  • версия кода без комментариев
  • аннотации
  • версия кода с построчными комментариями
  • дополнительные материалы
  • пример использования

Рассмотрим следующий код, определяющий класс валидации. Некоторые методы используют jQuery.

В редакторе нет подсветки Coffee, использовал питоновскую.

class validation 	find: (rules, rule) -> 		rules = rules.split '|' 		(rule in rules) 	validate: (rules) -> 		$('.form_error').remove() 		delete @errors_list 		@fields = {} 		@data = {} 		for field, rule of rules 			@fields[field] = $("input[name=\"#{field}\"]"); 			@data[field] = @fields[field].val() 			@validate_field field, @data[field], rules_string 		unless @errors_list?  			@data 		else 			@show_errors() 			false 	show_errors: -> 		focus_set = false 		for field, errors of @errors_list 			messages = [] 			for error in errors  				messages.push @lang error 			@fields[field].before nano templates.form_error, {message: messages.join(' ')} 			unless focus_set 				@fields[field].focus() 				focus_set = true 	lang: (error) -> 		[rule,value] = @rule_value error 		if value? 			nano lang.errors[rule], {val: value}) 		else 			lang.errors[rule] 	rule_value: (rule)-> 		x = rule.split '[' 		if x[1]? 			if x[1].substr(x[1].length - 1) == ']' 				x[1] = x[1].substr 0, x[1].length - 1 		[x[0],x[1]] 	min_length: (str,length) -> 		(str.length >= length) 	max_length: (str,length) -> 		(str.length < length) 	valid_email: (str) -> 		(/^[\w\d_-]+@+[\w\d_-]+\.+[\w]/i.test(str)) 	required: (str) -> 		(@min_length str, 1) 	min_value: (num,value) -> 		(num > value) 	max_value: (num,value) -> 		(num < value) 	numeric: (num) ->  		(/[^\d]/g.test(num)) 	alpha_numeric: (str) -> 		(/[^\da-z]/gi.test(str)) 	trim: (str) -> 		str.replace /^\s+|\s+$/g, '' 	integer: (str) -> 		parseInt(str) 	parse_rules: (rules) -> 		rules.split '|' 	validate_field: (field,str,rules) -> 		rules = @parse_rules rules 		for rule_string in rules 			[rule,value] = @rule_value(rule_string) 			result = @[rule] str, value 			unless result in [true,false] # post processing 				str = @data[field] = result 			else unless 'required' not in rules and str.length is 0 				if result is no 					@set_error field, rule_string 					break 	set_error: (field,rule) -> 		if @errors_list is undefined 			@errors_list = {} 		if @errors_list[field] is undefined 			@errors_list[field] = [] 		@errors_list[field].push rule 

Каждый из методов этого класса может быть использован сам по себе. Естественно, можно изменять и дописывать собственные методы – вместе с CoffeeScript это легко и занимательно.

Небольшой ликбез. Метод класса – это тоже самое, что функция класса. В JavaScript (и в CoffeeScript тоже, разумеется) доступен после инициализации класса через оператор доступа – точку. Примеры ниже.

Метод find позволяет найти указанное правило в строке определения правил валидации, написанной в духе CodeIgniter. В конкретном проекте, где в качестве бекенда выступил CI, мне было удобно непосредственно экспортировать объект правил в клиент.

Метод validate, принимающий список полей и правил, для каждого элемента формы выполнит очистку данных и проверку соответствия правилам. В случае успешного прохождения валидации метод вернет объект с чистыми данными. В случае обнаружения ошибок метод вызывет брата show_errors() и вернут false. Данные и ошибки сохраняются, как свойства класса. Поэтому они доступны для дальнейшего использования, и по этой же причине они очищаются перед началом.

Метод show_errors вставит сообщения об ошибках рядом с полями формы, и установит фокус (курсор) в первое поле, содержащее ошибку.

Метод lang найдет подходящее сообщение об ошибке, вставит в него значение из правила, если оно есть, и вернет строку.

Метод rule_value вернет значение из правила. Сделано на скорую руку, наверное можно сделать значительно красивее посредством регулярных выражений. Если у вас есть время и понимание – напишите в ЛС или комментарии, проверим и обновим код.

Методы min_length, max_length, valid_email, required, min_value, max_value, numeric и alpha_numeric проводят проверку с соответствующими правилами. Вы легко можете добавить методы.

Как и в CodeIgniter, валидация не только проверяет данные, но и очищает их. В данном классе это делают методы trim и integer.

Элементарный метод parse_rules принимает строку правил, отформатированную в духе CodeIgniter, и преобразует ее в массив.

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

Метод set_error просто запоминает ошибку.

Теперь версия кода с построчными комментариями.

class validation 	find: (rules, rule) -> 		# преобразует строку в массив, разделив по символу | 		rules = rules.split '|' 		# вернет результат проверки - есть ли rule в массиве rules 		(rule in rules) 	validate: (rules) -> 		# удалит все отбражаемые сообщения об ошибках - с классом .form_error 		$('.form_error').remove() 		# удалит список ошибок 		delete @errors_list 		# очистит локальный список ссылок на поля формы 		@fields = {} 		# создает (очищает) локальный объект данных 		@data = {} 		# пройдет в цикле все правила 		for field, rule of rules 			# сохранит ссылку на поле формы (просто для быстродействия) 			@fields[field] = $("input[name=\"#{field}\"]"); 			# извлечет данные из поля формы 			@data[field] = @fields[field].val() 			# проведет валидацию 			@validate_field field, @data[field], rules_string 		# если локальный список ошибок не существует 		unless @errors_list?  			# вернет данные 			@data 		else 			# вызовет метод для отображения сообщений об ошибках 			@show_errors() 			# вернет false 			false 	show_errors: -> 		# отметит, что фокус еще никуда не устанавливался 		focus_set = false 		# обойдет в цикле объект с ошибками 		for field, errors of @errors_list 			# тут у нас будут сообщения об ошибках 			messages = [] 			for error in errors  				# преобразует сообщения об ошибках в читаемый вид 				messages.push @lang error 			# перед каждым полем вставит сообщения об ошибках, связанные с этим полем 			@fields[field].before nano templates.form_error, {message: messages.join(' ')} 			# поставит фокус в первое поле, с которым связаны сообщения об ошибках 			unless focus_set 				@fields[field].focus() 				focus_set = true 	lang: (error) -> 		# получит значение из правила, если оно есть 		[rule,value] = @rule_value error 		if value? 			# обработает строку со значением 			nano lang.errors[rule], {val: value}) 		else 			# если значения нет - вернет языковую строку, как есть 			lang.errors[rule] 	rule_value: (rule)-> 		# обработает конструкцию вида min_length[3] 		x = rule.split '[' 		if x[1]? 			if x[1].substr(x[1].length - 1) == ']' 				x[1] = x[1].substr 0, x[1].length - 1 		[x[0],x[1]] 	min_length: (str,length) -> 		# вернет true, если длина строки больше или равна заданному значению 		(str.length >= length) 	max_length: (str,length) -> 		# вернет true, если длина строки меньше заданного значения 		(str.length < length) 	valid_email: (str) -> 		# вернет true, если строка соответствует паттерну 		(/^[\w\d_-]+@+[\w\d_-]+\.+[\w]/i.test(str)) 	required: (str) -> 		# вернет true, если строка не пустая 		(@min_length str, 1) 	min_value: (num,value) -> 		# вернет true, если число больше заданного 		(num > value) 	max_value: (num,value) -> 		# вернет true, если число меньше заданного 		(num < value) 	numeric: (num) ->  		# вернет true, если строка содержит только цифры 		(/[^\d]/g.test(num)) 	alpha_numeric: (str) -> 		# вернет true, если строка содержит только цифры и буквы 		(/[^\da-z]/gi.test(str)) 	trim: (str) -> 		# очистит строку от ведущих и замыкающих пробельных символов 		str.replace /^\s+|\s+$/g, '' 	integer: (str) -> 		# приведет в целочисленный вид 		parseInt(str) 	parse_rules: (rules) -> 		# преобразует строку правил в массив  		rules.split '|' 	validate_field: (field,str,rules) -> 		# преобразует строку правил в массив  		rules = @parse_rules rules 		# в цикле исследует все предоставленные правила 		for rule_string in rules 			# получит значение из правила, если оно есть 			[rule,value] = @rule_value(rule_string) 			# вызовет колбек для обработки или проверки 			result = @[rule] str, value 			# если результат обработки - что-то, кроме false или true 			# - значит была обработка, а не проверка 			unless result in [true,false] 				# перепишем результат 				str = @data[field] = result 			# если не просили пустую строку, но ничего не получили 			else unless 'required' not in rules and str.length is 0 				if result is no 					# ловим ошибку 					@set_error field, rule_string 					# а смысл дальше искать?  					# Если хотите - уберите эту строку, 					# мне нужна была полная аналогия с CI 					break 	set_error: (field,rule) -> 		# создаст объект ошибок, если его нет 		if @errors_list is undefined 			@errors_list = {} 		# создаст ветку об ошибках конкретного поля, если его еще нет 		if @errors_list[field] is undefined 			@errors_list[field] = [] 		# запомнит сообщение об ошибке 		@errors_list[field].push rule 

Используются сообщения об ошибках, хранящиеся в объекте, как показано ниже.

lang =  	errors: 		min_length: 'Недостаточная длина, нужно как минимум {val} симв.' 		max_length: 'Слишком длинная строка, допускается максимум {val} симв.' 		min_value: 'Слишком малое значение, нужно как минимум {val}.' 		max_value: 'Слишком большое значение, нежно не более {val}.' 		numeric: 'Допускаются только цифры.' 		alpha_numeric: 'Допускаются только цифры и буквы латиницы.' 		valid_email: 'Нужно указать правильный адрес электронной почты.' 		required: 'Здесь необходимо указать данные.' 

Для обработки сообщений об ошибках используется популярный минималистичный JS-шаблонизатор, вот его код, уже на CoffeeScript

_nano_regex = /\{([\w\.]*)\}/g nano = (template, data) -> 	template.replace _nano_regex, (str, key) -> 		keys = key.split(".") 		value = data[keys.shift()] 		$.each keys, -> 			value = value[this] 		(if (value is null or value is `undefined`) then "" else value) 

Оффтоп. Вот вам небольшой подарок за то, что вы читаете эти строки:

add_quotes = (str) -> 	(if str? then str.replace /"/g, """ else '') 

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

Допустим, у нас есть такие поля в форме

<label>Имя</label> <input type=”text” name=”name” value=”Василий”> <label>Фамилия</label> <input type=”text” name=”surname”> <label>Электропочта</label> <input type=”text” name=”email”> 

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

rules = 	name: 'trim|required|min_length[2]|max_length[255]' 	surname: 'trim|max_length[255]' 	email: 'trim|required|valid_email|max_length[255]'  validation = new validation  $('body').on 'submit', 'form', => 	data = validation.validate rules 	if data isnt off 		# отправляем их аяксом на сервер 	else 		# ошибки приключились. В принципе, можем больше ничего не делать – появятся сообщения об ошибках и даже курсор встанет в нужное поле 

Спасибо за внимание и удачи вам!

ссылка на оригинал статьи http://habrahabr.ru/post/184344/


Комментарии

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *