В статье приведено решение и описано его использование. Заказывайте темы для следующих статей в ЛС.
Тут можно получить информацию
- примеры использования 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) ->
(/^[wd_-]+@+[wd_-]+.+[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, если строка соответствует паттерну
(/^[wd_-]+@+[wd_-]+.+[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
# ошибки приключились. В принципе, можем больше ничего не делать – появятся сообщения об ошибках и даже курсор встанет в нужное поле
Спасибо за внимание и удачи вам!
Автор: customtema