Slide 1

Slide 1 text

Валидация данных на клиенте МАКСИМ КЛИМИШИН CTO at GVMachines @maxmaxmaxmax Tuesday, June 25, 13

Slide 2

Slide 2 text

Здрасьте, это я Background ‣ Lead both-end developer @oDesk Inc. ‣ Front-end consultant @Helios ‣ Conferences organizer Tuesday, June 25, 13

Slide 3

Slide 3 text

В теме? Background ‣ Любовь в основном с Angular.js ‣ Очень стараюсь смотреть на вещи трезво, а не инженерно ‣ Активно учавствую в украинских Python и JavaScript движениях Tuesday, June 25, 13

Slide 4

Slide 4 text

Интересно оглянуться назад и проанализировать почему валидация данных эволюционировала. Где же вы, попапы и конфирмы? Зачем все это? Tuesday, June 25, 13

Slide 5

Slide 5 text

ЧЕ? Topic ‣ Концептуально валидация нужна для работы с корректными данными внутри ПО ‣ Мотивация валидации на клиенте намного сложнее: это UI, время отклика интерфейса, комфорт в разработке, субъективное представление об интерфейсе Tuesday, June 25, 13

Slide 6

Slide 6 text

Начнем издалека Поначалу валидация была очень проста (CGI, Perl) $success = $dbh->do( "INSERT INTO $serverTabl(". "name,author,url,keyword1,keyword2,keyword3,". "keyword4,keyword5) VALUES(?,?,?,?,?,?,?,?)", undef,$name,$author,$url,$keyword[0],$keyword[1], $keyword[2],$keyword[3],$keyword[4]); if($success != 1) { return "Sorry, the database was unable to add your entry. ". "Please try again later."; } else { return; } Tuesday, June 25, 13

Slide 7

Slide 7 text

История Потом немного полегчало: Tuesday, June 25, 13

Slide 8

Slide 8 text

Netscape 2, давно это было История Самая простая валидация в виде alert-ов Tuesday, June 25, 13

Slide 9

Slide 9 text

Adobe Flash, Adobe Flex, MooTools, jQuery etc. После появления разного рода фреймворков все стало относительно просто: ‣ Валидацию начали аккуратно встраивать на серверных фреймворках (Zend Forms, Struts, Adobe Flex, Django) ‣ На клиенте начали “вручную” создавать довольно навороченные валидации с кошмарным кодом. Tuesday, June 25, 13

Slide 10

Slide 10 text

История ‣ Требует ресурс сервера, неприятно если контекст запросов достается из БД (сессии, контент страницы т.п.) ‣ Плохое время отклика - перезагрузки всей страницы ‣ Stateless-природа HTTP вынуждает хранить контекст всей страницы при сложном data- flow Если client-side валидация так хороша - почему от нее отказались? Tuesday, June 25, 13

Slide 11

Slide 11 text

История ‣ Плохой UX - большое время загрузки страницы ‣ AJAX может сильно помочь, но существенно усложнит логику на сервере и клиенте С точки зрения UX Tuesday, June 25, 13

Slide 12

Slide 12 text

История solid inline-validation против interactive inline-validation В цифрах 30%-50% против 80-100% Tuesday, June 25, 13

Slide 13

Slide 13 text

Современная валидация Tuesday, June 25, 13

Slide 14

Slide 14 text

Пример на Zakaz.ua Tuesday, June 25, 13

Slide 15

Slide 15 text

“You’d rather know about your mistakes as you go along.” Tuesday, June 25, 13

Slide 16

Slide 16 text

Stupid. “Captcha Makes It Impossible For The Blind To Sign Online Petition Supporting Copyright Treaty For The Blind” Someone from Twitter Не надо переусердствовать Tuesday, June 25, 13

Slide 17

Slide 17 text

Глаз ‣ Человеческий глаз видит 10-12 отдельный изобажений в секунду ‣ Все что меняется быстрее 50ms - незаметно ‣ The memory-prediction framework - мозг строит прогноз-ожидание (via Jeff Hawkins) Немного о высоком Tuesday, June 25, 13

Slide 18

Slide 18 text

Итого ‣ Сделать зрению комфортно. ‣ Удобно программировать ‣ Не нагружать сервер дополнительной логикой Короче, чего мы хотим Tuesday, June 25, 13

Slide 19

Slide 19 text

jQueryValidation jQueryValidation ‣ Позволяет декларативно задавать валидацию ‣ Можно научить кастомно показывать ошибки Tuesday, June 25, 13

Slide 20

Slide 20 text

jQueryValidation ... $(".selector").validate({ invalidHandler: function(event, validator) { var errors = validator.numberOfInvalids(); if (errors) { var message = errors == 1 ? 'You missed 1 field. It has been highlighted' : 'You missed ' + errors + ' fields.'; $("div.error span").html(message); $("div.error").show(); } else { $("div.error").hide(); } } }); Tuesday, June 25, 13

Slide 21

Slide 21 text

jQueryValidation Пацанский подход. Tuesday, June 25, 13

Slide 22

Slide 22 text

Давайте вернемся в 2013 год Backbone.js ‣ В моделях Backbone.js есть метод validate, а также при вызове метода set модели можно ловить change:property ‣ Есть плагин backbone.validation, позволяет в декларативном стиле задавать правила Tuesday, June 25, 13

Slide 23

Slide 23 text

Backbone.js Backbone.js var SomeModel = Backbone.Model.extend({ validation: { 'name': {required: true}, 'address.street': {required: true}, 'address.zip': {length: 4}, 'age': {range: [1, 80]}, 'email': {pattern: 'email'}, 'someAttribute': function(value) { if(value !== 'somevalue') return 'Error message'; } }}); var Person = Backbone.Model.extend({ // If you return a string from the validate function, // Backbone will throw an error validate: function( attributes ){ if( attributes.age < 0 && attributes.name != "Dr Manhatten" ){ return "You can't be negative years old"; } } Tuesday, June 25, 13

Slide 24

Slide 24 text

Но есть проблемы Backbone.js Tuesday, June 25, 13

Slide 25

Slide 25 text

Но есть проблемы Backbone.js ‣ Нужно имплементить отображение на уровне view ‣ Интерактивное отображение ошибок вынуждает писать больше кода ‣ В случае дочерних view сложность кода сильно возрастает (ловить event-ы) Tuesday, June 25, 13

Slide 26

Slide 26 text

Angular.js Angular.js ‣ Хорошие возможности валидации задаются декларативно из коробки - email, number, regexp. Красота, которая из коробки в html5 ‣ Кастомная валидация имплементится в виде директив, что обеспечивает надежный уровень изоляции ‣ Все валидируется “на лету”, к полям автоматически добавляются соотв. классы в случае ошибки (или ошибок) Tuesday, June 25, 13

Slide 27

Slide 27 text

Angular.js Angular.js Tuesday, June 25, 13

Slide 28

Slide 28 text

Angular.js Angular.js Tuesday, June 25, 13

Slide 29

Slide 29 text

Angular.js Tuesday, June 25, 13

Slide 30

Slide 30 text

Декларативненько на борту Angular.js ‣ Input types: email, number, url ‣ ngPattern – проверерка по regexp-у ‣ ngRequired, ngMinlengh, ngMaxlength ‣ ngList – преобразует строку с разделителем в массив (по умолчанию разделитель - запятая) ‣ ngTrim – пробелы в начале и конце строки игнорируются Tuesday, June 25, 13

Slide 31

Slide 31 text

А если AJAX? Angular.js ‣ Сложность резко возростает ‣ Нужно имплементить директиву ‣ и хорошо понимать что происходит в процессе валидации Tuesday, June 25, 13

Slide 32

Slide 32 text

Angular.js userinfo.directive('ajaxValidate', ['$http', function($http) { return { restrict: 'A', require: 'ngModel', link: function(scope, elem, attrs, ctrl) { var url = attrs.ajaxValidateUrl; var isEnabled = scope[attrs.ajaxValidate]; var messageId = attrs.name + "_invalid"; var actions = { error: function (value, data) { ctrl.$setValidity('ajaxvalidate', false); scope[messageId] = data.errors.value; return undefined; }, success: function (value, data) { ctrl.$setValidity('ajaxvalidate', true); return value; } }; ... Tuesday, June 25, 13

Slide 33

Slide 33 text

Angular.js ... ctrl.$parsers.push(function(value) { scope[messageId] = ''; if (isEnabled !== true) return; ctrl.$setValidity('ajaxvalidate', false); elem.attr("readonly", "readonly"); // doing HTTP request to validate value $http.post(url, {value: value}) .success(function(data) { elem.removeAttr("readonly"); return actions[data.action](value, data); }) .error(function (data) { elem.removeAttr("readonly"); alert("Something went wrong..."); }); }); } }; }]); Tuesday, June 25, 13

Slide 34

Slide 34 text

Angular.js

{{ imei_invalid }}

Tuesday, June 25, 13

Slide 35

Slide 35 text

Angular.js Angular.js Tuesday, June 25, 13