Хочу поделиться с общественностью своей небольшой (всего 6 Кбайт) js-библиотекой, которая сильно облегчает мне работу с формами при разработке сайтов, и позволяет сократить написание кода.
В любом, более-менее среднем и крупном проекте насчитается не один десяток форм, многие из которых желательно отправлять через AJAX. Часто для этого просто вешается обработчик и пишется скрипт, что-то типа этого:
$(function () {
$('.contact-submit').click(function () {
$.ajax({
url: '/path/to/action',
method: 'post',
data: $(this).closest('form').serialize(),
success: function () {
/* success action */
}
});
});
});
И вроде всё работает, но, к сожалению, очень часто разработчики забывают мелочи, такие как:
- отсутствие индикации отправки формы;
- как следствие, возможность накликать множество запросов (повторная отправка не блокируется);
- невозможность отправки формы с клавиатуры;
- отсутствие индикации при ошибке отправки формы (например, сервер ответил 500-ой ошибкой);
- хардкод url контроллера в js-скрипте.
Кроме того, есть множество тонкостей, которые не учтены в большинстве аналогичных библиотек, такие как: передача
name=value
кнопки, осуществляющей отправку формы, поддержка <input type="image">
и атрибутов form, formaction, formmethod
.А учитывая то, что форм в крупных проектах достаточно много, то такие обработчики для каждого типа форм в результате образуют весомый объём кода. А ведь часто, формы достаточно простые, такие как «форма обратной связи», нам нужно всего лишь отправить форму и показать статус — отправлено письмо или возникла ошибка. С помощью описываемой в данной статье библиотеки можно вовсе не писать код для таких простых форм.
Приступлю к описанию возможностей.
Поддержка html5 атрибутов для старых браузеров
Благодаря атрибутам
form, formaction, formmethod, formenctype, formtarget
можно гибко изменять поведения отправки формы. Самый часто встречающийся пример — выбор операции над множеством объектов, выбираемых пользователем с помощью checkbox:<div class="items">
<div class="item">
<input type="checkbox" form="mass"> Выбрать
<form action="/cart/add">
<input type="text" value="1">
<button type="submit">Добавить в корзину</button>
</form>
</div>
<div class="item">
<input type="checkbox" form="mass"> Выбрать
<form action="/cart/add">
<input type="text" value="1">
<button type="submit">Добавить в корзину</button>
</form>
</div>
...
<form id="mass" method="post">
<button type="submit" formaction="/controller/move">Переместить</button>
<button type="submit" formaction="/controller/delete">Удалить</button>
</form>
</div>
С пощью атрибутов
form
и formaction
мы смогли решить проблему невозможности вложенных форм, а также выбор url контроллера в зависимости от действия.Библиотека предоставляет polyfill для браузеров, которые не поддерживают данные атрибуты (IE<10).
Демо
Блокировка повторной отправки и отображение состояния
Думаю, с этим встречались многие не раз, что пользователь нажимает кнопку отправки формы несколько раз, и в результате вместо, например, одного комментария, публикуются сразу три одинаковых. Очень часто, чтобы избежать подобного эффекта, просто пишут скрипт, который делает кнопку отправки disabled после нажатия, забывая при этом, что форму можно ещё отправить с клавиатуры.
Библиотека также добавляет класс
form-loading
когда происходит процесс отправки, благодаря этому можно с помощью CSS застилить это состояние формы так, как захочется.Если всё же требуется возможность повторной отправки формы до завершения предыдущей, то достаточно добавить класс
form-no-block
.Демо
Фильтрация пустых полей при отправке
Данная возможность может пригодиться для форм поиска/фильтрации товара, в которых много полей, для того, чтобы URL выглядели более наглядно. Согласитесь, что увидеть в адресной строке лучше
http://example.com/?price_to=1000
чем
http://example.com/?price_to=1000&price_to=&amount_from=&amount_to=
Просто добавьте класс
form-no-empty
к форме.Демо
AJAX отправка формы
Это, пожалуй, одна из главных функций библиотеки. Отправлять формы через AJAX умеют многие плагины, а вот отправлять форму также, как это делает браузер — к сожалению, нет. В частности, в ней реализована поддержка описываемых выше html5 атрибутов
form
, formaction
, formmethod
, передача параметров активной submit-кнопки и координат для <input type="image">
. Для того, чтобы передача формы осуществлялась через AJAX надо добавить CSS-класс form-ajax
к форме, а для отображения результата — добавить в произвольное место внутри формы тег <output>
или другой тег с классом form-output
. Можно указать элемент вне формы, для этого в атрибуте data-form-output
указать jQuery-селектор этого элемента. Аналогичный атрибут можно использовать и в корневом элементе ответа (он более приоритетный). А если корневой элемент ответа имеет CSS-класс form-replace
, то в качестве элемента вывода будет использована сама форма, причём она заменится, что очень удобно для «одноразовых» форм. Рассмотрим это на примере формы обратной связи:<form action="/site/callback" method="post" class="form-ajax">
<div class="form-group">
<label>Имя</label>
<input type="text" name="name">
</div>
<div class="form-group">
<label>Email</label>
<input type="text" name="email">
</div>
<div class="form-group">
<label>Сообщение</label>
<textarea name="message"></textarea>
</div>
<output></output>
<button type="submit">Отправить</button>
</form>
// контроллер на примере Yii 2
public function actionCallback() {
$model = new CallbackForm();
$model->load(Yii::$app->request->post())
if ($model->validate()) {
// тут отправляем письмо, успех статус в $success
if ($success) {
return '<div class="alert alert-success form-replace">Сообщение отправлено</div>';
}
}
return '<div class="alert alert-danger">Ошибка отправки</div>';
}
Алгоритм такой — форма отправляется через AJAX, и если она проходит валидацию и ошибок в отправки нет, то выводим Bootstrap Alert что всё в порядке, причём он заменит собой форму. Если же есть какие-то ошибки, выводим их в тег
<output>
. И заметьте, нет ни строчки JavaScript. Библиотека имеет поддержку Bootstrap Alert, так, что он автоматически делает alert-dismissible
и самостоятельно добавляет кнопку закрытия.Для AJAX-форм генерируются следующие события:
formajaxbefore
— перед отправкой формы, позволяет изменить все настройки$.ajax
;formajaxdone
— в случае успешной отправки;formajaxfail
— в случае возникновения ошибки;formajaxalways
— в любом случае, после завершения AJAX-запроса.
В целом, это тоже облегчает написание своих скриптов, т. к. позволяет навешивать код прямо на событие успешного ответа, минуя рутину с передачей URL и параметров. В событиях можно отменить поведение скрипта по-умолчанию, достаточно вызвать
e.preventDefault()
— никаких манипуляций с выводом контента не будет происходить.Если же, наоборот, подгружаемый контент нужен и, более того, содержит другие компоненты, которые надо проинициализировать (например, datepicker). Для этого библиотека генерирует ещё два события:
contentprepare
— до добавления контента в DOM;contentinit
— после добавления контента в DOM.
Для себя я сделал вывод, что мне проще иметь одинаковую процедуру инициализации всех плагинов, поэтому пришёл к такому процессу:
$(function () {
$(document).trigger('contentinit', [$(document)]);
});
$(document).on('contentinit', function (e, cont) {
cont.find('.datepicker').datepicker();
});
То есть при загрузке страницы мы также генерируем событие
contentinit
, на который навешиваем обработчик, инициализирующий все плагины.Демо
AJAX отправка файлов
Отправить без перезагрузки страницы форму с файлами ещё сложнее, т. к. устаревшие браузеры этого вообще не умеют, и тут нужен iframe-метод для эмуляции этого процесса. Здесь я не стал изобретать велосипед, и отдал данный функционал полностью на плечи malsup jquery.form плагину, оставив лишь единый способ его отправки. На стороне сервера всё выглядит также, как если бы вы отправляли формы стандартным способом.
Чтобы отправить форму с файлами, необходимо подключить malsup jquery.form плагин и указать enctype=«multipart/form-data». Для пересылаемой данным способом формы дополнительно будет генерироваться событие:
formajaxprogress
— событие, информируещее о текущем состоянии отправки.
Демо
AJAX редирект
Ещё одна небольшая полезная мелочь. Чтобы заставить страницу перейти на новую (осуществить редирект), просто укажите заголовок
X-Redirect
, значение которого — URL для перехода.Демо
Кнопка, для классического субмита в AJAX-форме
Можно сделать так, что в AJAX-форме будет одна или несколько кнопок, которые отправляют форму обычным способом. Например, я это использую на странице корзины — когда пользователь меняет количество товара, то применяется AJAX для сохранения изменений. Когда же он нажимает кнопку «Перейти к оформлению», происходит обычная отправка той же формы.
Демо
Изменение внешнего вида кнопки отправки формы
Библиотека предоставляет ещё несколько хелперов для того, чтобы изменить вид кнопки отправки для того, чтобы пользователи понимали, что форма в процессе отправки. Для этого добавьте к кнопке CSS-класс
btn-loading
, а также используйте одну или обе возможности:- aтрибут
data-loading-text
— меняет текст кнопки во время отправки формы на указанный в атрибуте, а после завершения, возвращает обратно; - aтрибут
data-loading-icon
— добавляет к кнопке значок, показывающий процесс загрузки.
Например:
<!-- font awesome -->
<button type="submit" class="btn btn-primary btn-loading" data-loading-icon="fa fa-refresh fa-spin">Submit</button>
<!-- bootstrap glyph -->
<button type="submit" class="btn btn-primary btn-loading" data-loading-icon="glyphicon glyphicon-refresh">Submit</button>
Дополнительно можно указать для одной или нескольких кнопок класс
btn-submit-default
, тогда если форма будет отправлена с клавиатуры, кнопки всё равно изменят свой вид.Демо
Алерты для форм
Иногда бывает нужно, что надо отобразить пользователю какие-то предупреждения или сообщения об ошибках в форме, например, об ошибках валидации. Библиотека предоставляет простой интерфейс для добавления bootstrap alert-ов в элемент вывода формы. Используется так:
$('form').formAlert('Ошибка! Заполните <b>обязательные поля</b>!', 'danger');
$('form').formAlert('#external-alert', 'success', true);
Демо
Заключение
Библиотека доступна в репозитариях npm и bower под наименованием
paulzi-form
:bower install paulzi-form
npm install paulzi-form
Она достаточно молодая, поэтому жду отчётов об ошибках на гитхабе. Буду рад, если кому-то скрипт тоже облегчит жизнь, как и мне.
Проект на GitHub
Комментарии (17)
shamanis
29.06.2015 08:39+16Половина «проблем» решается, если обработчик вешать не на click, а все-таки на submit.
DeadikGudwin
29.06.2015 09:43+4а отправка файлов решается использованием formData вместо сериализации
PaulZi Автор
29.06.2015 10:04FormData доступен начиная с XMLHttpRequest 2. Никто не спорит что всё это можно решить руками, библиотека лишь предоставляет удобный способ вообще не писать скрипты для этого, и не думать как это будет работать, если форма передаётся через AJAX.
firya
29.06.2015 12:58+1а «хардкод url контроллера в js-скрипте» решается, например так:
url: form.attr("action"),
past
29.06.2015 09:57-1Что на счет websockets?
PaulZi Автор
29.06.2015 10:07+1Не понял, как и для чего вы хотите применить здесь WebSockets?
past
29.06.2015 11:13Вместо XMLHttpRequest.
Как транспорт для общения с сервером.PaulZi Автор
29.06.2015 11:20+1А что мы от этого выигрываем? Поддержка браузерами веб-сокетов ничуть не лучше даже чем XMLHttpRequest 2. Если же нужна постоянная двусторонняя связь с сервером, а не отправка формы, для чего WebSockets и создавались, то это уже совсем другая задача.
Xao
29.06.2015 12:25Вроде же, начиная с IE8 всё поддерживается. Вдогонку, там всякие оперы и фаерфоксы тех времён, хром и подавно.
AlexP11223
29.06.2015 12:518? Да ну? caniuse.com/#feat=websockets
dolphin4ik
29.06.2015 10:14Запретить повторную отправку можно и кукисами из js, ну и на сервере ещё раз проверить
Magistr1976
То что надо!