Привет, Хабр!
Как-то, год назад я писал о своей небольшой библиотеке, которая облегчает разработку форм на сайтах. Недавно я выпустил 3-ю версию, которая, по-сути, была переписана с нуля, чтобы стать правильней и удобней. Но в своей статье я не буду повторять README и ДЕМО, а лучше покажу на практике, каким образом она помогает писать меньше кода.
Но прежде, хотелось бы дать немного информации о новой версии. В ней, я избавился от зависимости malsup jquery.form для отправки файлов, и один большой репозитарий я разделил на несколько мелких самостоятельных репозитариев, сохранив, при этом, общую сборку "всё-в-одном":
- поддержка дополнительных событий форм (form-extra-events);
- полифил HTML5 form-атрибутов (form-association-polyfill);
- поддержка отправки файлов через iframe для старых браузеров (jquery-iframe-ajax);
- и, собственно, сама библиотека (paulzi-form).
Дополнительные события форм
Во второй версии библиотеки я столкнулся с проблемой необходимости соблюдения определённого порядка подключения скриптов. Например, раньше нельзя было подключать данный скрипт перед стандартным скриптом валидации Yii2, дело в том, что на событие submit навешиваются все скрипты, которые так или иначе должны обработать отправку, в частности произвести валидацию, и нет никакой гарантии что кто-то это событие не прервёт вызовом
preventDefault()
. Поэтому, в случае неверного порядка подключения, сначала происходила блокировка формы, а затем валидация, и, если были ошибки валидации, повторно отправить форму было уже нельзя. form-extra-events решает эту проблему, он предоставляет несколько новых типов событий формы, одно из которых гарантирует то, что форма отправляется, и этот процесс уже нельзя прервать. Кроме того, генерируются события начала и завершения отправки формы, что используется во всех остальных возможностях библиотеки.
Сайт-прототип

http://paulzi.ru/paulzi-form-site/
Из скриптов используем только jQuery, bootstrap и paulzi-form.all.js. В данном прототипе мы не используем ни строчки JS-кода написанного специально для сайта-прототипа.
Html5 form-атрибуты

switch($action)
, вместо того, чтобы сразу направить на конкретный action (например, в Yii2). А если вы откроете модальное окно, то увидите, что кнопку отправки пришлось сделать вне самой формы, тем не менее она продолжает функционировать, так как ей был указан атрибут form
. А самое главное, данные атрибуты сильно выручают в ситуациях, когда в большой форме нужно сделать маленькую форму, что стандарт HTML запрещает.
Не отправлять пустые поля

?proce_from=&proce_to=&tdp_from=&tdp_from=&line[]=i5
Используя библиотеку, если добавить к форме атрибут data-submit-empty="false"
незаполненные поля не будут отправляться, и в результате получится более человекопонятный URL:
?line[]=i5
Блокировка повторной отправки

data-lock="false"
Индикация состояния отправки

data-loading-text
и data-loading-icon
, которые изменяют текст кнопки и добавляют иконку. Также к форме и кнопке добавляются классы form-loading
и btn-loading
, это позволяет застилизовать состояние через CSS.
AJAX-отправка формы
Ну и самое главное, это возможность отправки формы через AJAX. Действительно, у нас в прототипе есть две формы в модальных окнах, логично при нажатии на кнопку отправки не осуществлять переходы между страницами, а просто закрыть модальное окно и вывести сообщение об успехе. И тут всё очень легко — добавляем атрибут
data-via="ajax"
, и вуаля, форма отправляется через AJAX. И мало того, те, кто хоть раз занимался передачей через AJAX файлов знает, что сделать это не так просто, т. к. поддержка отправки файлов появилось только начиная с XMLHttpRequrest 2. Для этого часто подключают сторонние библиотеки, которые часто требуют написания на серверной части особых обработчиков, хранить файлы во временной папке, а потом при отправке формы в отдельном запросе их оттуда забирать. В нашем же случае, об этом практически не надо задумываться — всё идёт также, как если бы форма отправлялась стандартным способом. При необходимости можно легко вывести процент отправленных данных, подцепившись на событие uploadprogress
.
Обработка AJAX-ответов

<span class="badge" data-insert-context="document" data-insert-to=".cart-block .badge" data-insert-mode="replaceWith">43</span>
<div class="alert alert-success alert-dismissible" role="alert" data-insert-context="document" data-insert-to=".alert-fixed">
<button type="button" class="close" data-dismiss="alert"><span aria-hidden="true">×</span></button>
Товар добавлен в корзину!
</div>
<div class="cart-block-hover" data-insert-context="document" data-insert-to=".cart-block-hover" data-insert-mode="replaceWith">
<div class="row">
<div class="col-xs-6 text-left">AMD K6</div>
<div class="col-xs-6">10 шт.</div>
</div>
<div class="row">
<div class="col-xs-6 text-left">Intel Celeron</div>
<div class="col-xs-6">12 шт.</div>
</div>
<div class="row">
<div class="col-xs-6 text-left">Intel Core i7</div>
<div class="col-xs-6">1 шт.</div>
</div>
<div class="row cart-block-total">
<div class="col-xs-6 text-left">Итого:</div>
<div class="col-xs-6"><span class="price"><span>117 000</span> ?</span></div>
</div>
</div>
Как видите, ответ состоит из трёх html-элементов с атрибутами data-insert-to
, data-insert-context
, data-insert-mode
. Эти атрибуты определяют как и в какое место вставить каждый элемент, например, для первого span.badge
data-insert-context="document" data-insert-to=".cart-block .badge" data-insert-mode="replaceWith"
означает, что данным элементом нужно заменить элемент .cart-block .badge
и зона поиска данным селектором — весь документ. Всё это позволяет провести описанные выше действия без единой строчки кода.
Сценарии

data-via
атрибут, который укажет как отправлять форму при нажатии на неё.
Настройки
Если вам что-то не нравится, или возникают какие-либо конфликты, в библиотеке можно многое поменять через настройки. Например, нужно ли блокировать повторную отправку всех форм по-умолчанию. Все названия атрибутов также можно поменять.
Заключение
В итоге, подключив библиотеку размером 10 кб, вы получаете самое главное — нужно меньше писать код для разных мелких форм (а то и вовсе не писать), а в довесок получаете несколько приятных возможностей, такие как дополнительные события форм, iframe-транспорт, AJAX-отправка файлов и др.
Комментарии (24)
Alexufo
24.07.2016 00:59-3Отлично. Давно подобное напрашивается. Обязательно буду смотреть.
Офигеть. 5 часов в эфире и всего +6 голосов за статью. Года три назад сотня была бы точно.dfuse
24.07.2016 10:41+1Потому что года три назад никто ничего не знал про Angular, React и прочие, а также про HTML5 File Api, WhatWG Fetch и тд. Зачем в 2016 году библиотека по работе с формами — не ясно.
PaulZi
24.07.2016 11:21-3Разве Angular, React и HTML5 File API предоставляют возможность отправлять формы через AJAX, без кучи `new XMLHttpRequest()` для каждой формы? Даже если отмести все старые браузеры (а далеко не всегда это возможно) и выкинуть из библиотеки `ajax-iframe-transport` и `form-attributes-polyfill`, всё равно остаётся много полезного функционала — ajax отправка форм без единой строчки кода, стилизация состояния отправки, возможность навешивать обработчики отправки форм после других скриптов и др.
Если вы найдёте простой способ это реализовать на react и angular без написания аналогичного скрипта — будет очень интересно посмотреть.dfuse
24.07.2016 11:33Смотрите
var f = new FormData(), d = document, byId = d.getElementById; f.append('text', byId('text').value); // текстовые значения f.append('file', byId('file').files[0]); // файлы из формы f.append('file2', new File([JSON.stringify(...whatever...)], 'request.json', {type: 'application/json'})); // произвольное барахло fetch('url', {method: 'post', body: f}).then(...).catch(...);
Вот и все, и работает почти везде.
Про стилизацию я вообще молчу — она у всех разная, на реальном сайте я быстрее напишу CSS с нуля, чем буду накручивать любой коробочный. А любые визуальные фидбеки отправки я навешу в одно действие в .then и .catch.PaulZi
24.07.2016 11:39+1Я могу такой же короткий пример написать на ультра-старом jQuery, но ваш пример не решает и половины проблем, которая решает библиотека. А как только вы их решите, ваш ультра короткий и лаконичный код распухнет как минимум до 4 кбайт, который надо будет тащить из проекта в проект.
PS В конструктор FormData пожно передать саму форму, тогда append() вообще не надо.dfuse
24.07.2016 11:47+4jQuery еще подключить надо. А вышеописанное — стандартная библиотека, которая есть везде, совместимая с любым React/Angular/PlainVanilla.
Библиотека решает надуманные проблемы, ничего общего не имеющие с реальным миром и реальными сайтами. Любой серьезный проект так или иначе будет иметь код, отвечающий за весь трафик, включая формы. А для проекта на коленке сгодится и прямое использование стандартных средств браузера. Получается, ни туда, ни сюда библиотека.
Прошли те годы, когда подобное было актуально, лет 5 назад вы бы получили море звезд и хвалебных отзывов, но времена поменялись.Gitkan
24.07.2016 14:39Тут есть другой критерий полезности/нужности — насколько часто встречаются формы на современных сайтах? Если это форма постинга в блог или форма отправки заказа в интернет-магазине, то все задачи решаются соответствующими движками из коробки. Но если у разработчика специфический заказ и у него на каждой странице по несколько форм (какая-нибудь не к ночи будет сказано online-ERP система), то библиотека и может быть полезна.
PaulZi
24.07.2016 11:44-1По поводу стилизации, по умолчанию просто добавляются CSS-классы в форму — стилизуй как хочешь. Кроме того, есть поддержка js-шаблонов, и на крайний случай, всегда можно подцепиться на события `submitstart` и `submitend` и застилизовать произвольным образом. То есть ничего «коробочно» не навязывается.
dfuse
24.07.2016 11:50+3А как насчет валидации (клиентской и серверной), а также распихивания ошибок по полям? Вы поймите, прошли те времена, когда юзеры брали скрипт и впендюривали его на страницу, сейчас все живут в некой экосистеме, будь то Angular, React, Knockout, Ember или любая другая, где есть уже шаблоны, есть событийная модель и тд и тп.
hlogeon
24.07.2016 14:22Angular, React и HTML5 File API предоставляют возможность отправлять формы через AJAX
Не могу со 100% уверенностью сказать за остальные помимо Angular(ибо я занимаюсь back-end), но со 100% уверенностью говорю, что Angular позволяет. Более того, вполне себе позволяет создавать целые сервисы и директивы с формами с вытекающими отсюда плюшками в виде реюзабельности и наследования. Думаю, что React тоже это позволяет.
Varkus
24.07.2016 11:07когда читаю, как в очередной раз, вместо того, чтобы хорошо знать язык сайтокодеры подключают очередные файлы к странице — печалюсь: это не сайт перегружен всяким барахлом, а телефон у вас тупой, браузер медленный, пальцы кривые, рожей не вышел и т.д.
P.S. вероятно это будет уже 5й коммент подряд не одобренный администрацией.kesn
24.07.2016 11:27+2Если вы про библиотеку автора — то прошу пояснить, что же конкретно в ней плохо. В ней собраны юзкейсы, которые встречаются постоянно. Why not?
PaulZi
24.07.2016 11:27+1Что-то я не понял, что вы хотели сказать своим комментарием? Откройте какой-нибудь мега современный сайт на angular/react (например tinkoff.ru) и прочем и посмотрите сколько там скриптов подключается. Современные сайты действительно имеют много JS-кода, без него сейчас красивого и удобного сайта не сделать.
rockin
24.07.2016 19:04Правда? :)
Хотите историю из жизни и с конкретным примером?
Есть такой, не побоюсь этого слова, крупнейший интернет-магазин компьютерных комплектующих в РФ. Да, это Ситилинк. В рекламе не нуждается, сами понимаете.
И был у него нормальный, лёгкий, быстрый сайт. Логичное меню, картинки на своих местах.
Не знаю, что там случилось, и кому конкретно в голову ударила жидкость жёлтого цвета, но…
сейчас там «расчудесное», криво работающее, да ладно там криво — еле-еле работающее меню. которое выезжает когда не надо, уезжает когда не надо и перекрывает полстраницы.
А ещё там есть .js… пока её не пожали, весила она более мегабайта(!)
Сейчас — 890кб.
http://static.citilink.ru/build/js/citilink.build.min.js
И css на 290кб
http://static.citilink.ru/build/styles/global/citilink.build.css
Был красивый и удобный сайт. Стал некрасивый и жутко неудобный. JS стало больше в разы. А толку намного меньше
А при заходе на юлмарт грузится js сервиса аналитики размером в 1200кб
Куда мир катится, я хз. Такое чувство, что все только дома сайтики открывают, а домики все подключены оптикой.
Xtray
24.07.2016 20:14-3Если имеют много JS, это же не значит, что это хорошо. Удобный сайт спокойно может без JS обойтись. Ну, или самый минимум, десяток-другой строк кода, без каких-либо фреймворков. А красота — вообще субъективна, а потому — да, и красивый сайт тоже можно без JS сделать.
nazarpc
На сколько древним должен быть браузер чтобы не уметь отправлять файлы через XHR? Мне казалось, это уже в прошлом и не актуально.
PaulZi
http://caniuse.com/#feat=xhr2