- Преимущества FetchIt перед FormIt и AjaxForm
- Установка
- Документация по сниппету FetchIt
- Параметры сниппета
- Пример вызова
- Посадка форм на FetchIt
- Прочая документация
- Селекторы
- data-fetchit
- data-error
- data-custom
- JS API
- Класс FetchIt
- Экземпляр класса FetchIt
- События
- Примеры разметок форм
- Примеры всплывающих сообщений
- Примеры модальных окон
- Примеры дополнительной валидации
- Смотрите также
Разберем довольно свежее MODX (v2 / v3) дополнение: FetchIt, которое позволяет отправлять формы при помощи Fetch API.
Преимущества FetchIt перед FormIt и AjaxForm
FormIt реализует отправку и обработку форм стандартным методом браузера, т.е. с перезагрузкой страницы. Что как обычно не есть хорошо, т.к. современный веб требователен и прогрессивным шагом является их обработка «на лету».
AjaxForm, отправляет по аякс, без всяких перезагрузок (обрабатывает на ленту), все прекрасно, но. Он имеет следующие зависимости: jquery (увесистая библиотека, которая с каждым годом все меньше и меньше используется) и плагины к ней jquery-form и jGrowl. Если ваш сайт на ванильном JS (например bootstrap 5 и прочих vanila js библиотеках), jquery и дополнения к нему уже получается нафиг не нужны, так как значительно ухудшают скорость сайта.
Недостатки конкурентов разобрали, переходим к гостю программы FetchIt, который использует по умолчанию FormIt в качестве обработчика для отправки форм + можно использовать свой скрипт (как и AjaxForm). Но у него нет зависимостей (хотя FetchIt по серверной части практически 1 в 1 похож на AjaxForm).
В добавок к этому его модифицированный скрипт весит всего 5 килобайт и сниппет регистрирует его с атрибутом defer, тем самым не мешая загрузке страницы. И в него намного проще встроить элементы вёрстки, добавление всплывающих сообщений и модальных окон.
В общем спасибо Бахе, получилась отличная замена АяксФорм.
Установка
Дополнение бесплатное, установка стандартная, загружен как на modstore.pro, так и на официальный репозиторий modx.com.
Документация по сниппету FetchIt
Данный сниппет отправляет форму с помощью Fetch API и запускает сниппет указанный в параметре snippet
передавая ему параметры. По умолчанию вызывает сниппет FormIt, но вы можете использовать и свой собственный. Также загружает на фронтенде скрипт обработки ответа сервера.
Параметры сниппета
Параметры | Значение по умолчанию | |
---|---|---|
form | tpl.FetchIt.example |
Чанк с формой, которую нужно обработать |
snippet | FormIt |
Запускаемый сниппет для обработки формы |
actionUrl | [[+assetsUrl]]action.php |
Адрес коннектора, на который отправляется форма |
clearFieldsOnSuccess | 1 |
Данный параметр отвечает за очистку данных формы после успешного ответа |
Все остальные параметры, которые вы укажете при вызове FetchIt будут переданы сниппету указанному в параметре snippet
.
Пример вызова
[[!FetchIt?
&form=`myForm.tpl`
&hooks=`email`
&emailSubject=`Тема письма`
&emailTo=`info@domain.com`
&validate=`name:required,email:required`
&validationErrorMessage=`В форме содержатся ошибки!`
&successMessage=`Сообщение успешно отправлено!`
]]
Посадка форм на FetchIt
Итак у нас есть вот такая html разметка формы:
<form action="#" method="post" class="php-email-form">
<div class="row gy-4">
<div class="col-md-6">
<input type="text" name="name" class="form-control" placeholder="Your Name" required>
</div>
<div class="col-md-6 ">
<input type="email" class="form-control" name="email" placeholder="Your Email" required>
</div>
<div class="col-md-12">
<input type="text" class="form-control" name="subject" placeholder="Subject" required>
</div>
<div class="col-md-12">
<textarea class="form-control" name="message" rows="6" placeholder="Message" required></textarea>
</div>
<div class="col-md-12 text-center">
<div class="sent-message">Your message has been sent. Thank you!</div>
<button type="submit">Send Message</button>
</div>
</div>
</form>
Выносим ее в чанк с именем contactForm
и адаптируем разметку под FetchIt.
Первым делом разметим элемент form, сейчас он выглядит так:
<form action="#" method="post" class="php-email-form">
Здесь в атрибуте action указан файл — php обработчик (который мы не загружали) — по идее его можно загрузить на сайт, отредактировать и использовать, но мы все будем обрабатывать при помощи FormIt. По этому в атрибуте action указываем [[~[[*id]]? &scheme=`full`]]
— данная конструкция подставляет туда полную ссылку на страницу. В конечном итоге получаем:
<form action="[[~[[*id]]? &scheme=`full`]]" method="post" class="php-email-form">
<form action="{$_modx->makeUrl($_modx->resource.id, '', '', 'full')}" method="post" class="php-email-form">
Теперь апгрейдим наши поля ввода textarea и input. Разберу на примере input, он сейчас выглядит так:
<input type="text" name="name" class="form-control" placeholder="Your Name" required>
Здесь уже есть обязательный атрибут name (если в вашей форме в полях ввода его нет, то нужно добавить, например: name="phone"
).
По поводу кнопки отправить, она всегда должна иметь атрибут type="submit"
(у нас он есть).
В принципе наш код должен работать. Сделаем минимальный вывод FetchIt (при условии что установлен formit):
[[!FetchIt?
&form=`contactForm`
&hooks=`email`
&emailTo=`[[++emailsender]]`
]]
form
contactForm — наша форма которую поместили в чанк и немного переделалиhooks
email — который отвечает за отправку формы на email;- в
emailTo
— указываем почту, куда отправлять письма (я указал системную настройку emailsender — в которой прописан email, а так его можно и в ручную указать).
Этого достаточно чтобы форма начала отправляться. Пробуем заполнить и отправить.
Форма отправляется, данные приходят, в не совсем читаемом виде, и даже есть уведомление: Форма отправлена (при условии, что включена системная настройка: Подключить дефолтную библиотеку уведомлений. — fetchit.frontend.default.notifier — Да).
Сейчас если убрать из разметки формы атрибуты required
, то ее можно будет отправлять и пустой. Давайте исправим это сделаем вывод ошибок, русифицируем плейсхолдеры и т.д.. После модернизации должна получится вот такая разметка формы:
Начнем с читаемости писем, создадим чанк tplForm
, со следующим содержимым:
<p><strong>Имя</strong>: [[+name]]</p>
<p><strong>Почта</strong>: <a href="mailto:[[+email]]">[[+email]]</a></p>
<p><strong>Тема</strong>: [[+subject]]</p>
<p><strong>Сообщение</strong>: [[+message]]</p>
<p><strong>Имя</strong>: {$name}</p>
<p><strong>Почта</strong>: <a href="mailto:{$email}">{$email}</a></p>
<p><strong>Тема</strong>: {$subject}</p>
<p><strong>Сообщение</strong>: {$message}</p>
Здесь мы выводим названия полей name="name"
.
Далее модифицируем разметку формы:
<form action="[[~[[*id]]? &scheme=`full`]]" method="post" class="php-email-form">
<div class="row gy-4">
<div class="col-md-6">
<input type="text" name="name" class="form-control" placeholder="Ваше Имя" value="[[+fi.name]]">
<span data-error="name">[[+fi.error.name]]</span>
</div>
<div class="col-md-6 ">
<input type="email" class="form-control" name="email" placeholder="Ваш Email" value="[[+fi.email]]">
<span data-error="email">[[+fi.error.email]]</span>
</div>
<div class="col-md-12">
<input type="text" class="form-control" name="subject" placeholder="Тема" value="[[+fi.subject]]">
<span data-error="subject">[[+fi.error.subject]]</span>
</div>
<div class="col-md-12">
<textarea class="form-control" name="message" rows="6" placeholder="Сообщение">[[+fi.message]]</textarea>
<span data-error="message">[[+fi.error.message]]</span>
</div>
<div class="col-md-12 text-center">
[[+fi.validation_error_message]]
[[+fi.successMessage]]
<button type="submit">Отправить сообщение</button>
</div>
</div>
</form>
<form action="{$_modx->makeUrl($_modx->resource.id, '', '', 'full')}" method="post" class="php-email-form">
<div class="row gy-4">
<div class="col-md-6">
<input type="text" name="name" class="form-control" placeholder="Ваше Имя" value="{$fi.name}">
<span data-error="name">{$fi.error.name}</span>
</div>
<div class="col-md-6 ">
<input type="email" class="form-control" name="email" placeholder="Ваш Email" value="{$fi.email}">
<span data-error="email">{$fi.error.email}</span>
</div>
<div class="col-md-12">
<input type="text" class="form-control" name="subject" placeholder="Тема" value="{$fi.subject}">
<span data-error="subject">{$fi.error.subject}</span>
</div>
<div class="col-md-12">
<textarea class="form-control" name="message" rows="6" placeholder="Сообщение">{$fi.message}</textarea>
<span data-error="message">{$fi.error.message}</span>
</div>
<div class="col-md-12 text-center">
{$fi.validation_error_message}
{$fi.successMessage}
<button type="submit">Отправить сообщение</button>
</div>
</div>
</form>
Я убрал из разметки input
атрибут required
— обязательное для заполнения поля (проверка на стороне браузера), мы будем проверять на стороне сниппета. Добавил к полям вывод ошибок сниппетом <span data-error="name_polya">[[+fi.error.subject]]</span>
. И добавил вывод сообщений о ошибке (validation_error_message
) и отправке (successMessage
).
Теперь чтобы наша разметка заработала модернизируем наш вывод:
[[!FetchIt?
&hooks=`email`
&validate=`name:required:maxLength=^21^,email:email:required,subject:required,message:required:minLength=^30^`
&form=`contactForm`
&emailTpl=`tplForm`
&emailTo=`[[-++emailsender]]`
&emailSubject=`Сообщение со страницы контакты на сайте [[++site_url]]`
&successMessage=`<div class="sent-message">Ваше сообщение было отправлено. Спасибо!</div>`
&validationErrorMessage=`<div class="error-message">Произошла ошибка при отправке вашего сообщения.</div>`
]]
{'!FetchIt' | snippet : [
'form' => 'contactForm',
'validate' => 'name:required:maxLength=^21^,email:email:required,subject:required,message:required:minLength=^30^',
'emailTpl' => 'tplForm',
'emailTo' => $_modx->config.emailsender,
'emailSubject' => 'Сообщение со страницы контакты на сайте' ~ $_modx->config.site_name,
'successMessage' => '<div class="sent-message">Ваше сообщение было отправлено. Спасибо!</div>',
'validationErrorMessage' => '<div class="error-message">Произошла ошибка при отправке вашего сообщения.</div>'
]}
Пробуем сначала отправить пустое сообщение:
Отлично. Теперь пробуем все заполнить и отправить.
Ну и на почту нам уже падает нормальное письмо, с темой и т.д.
Прочая документация
Селекторы
Ниже находится информация о селекторах, с которыми взаимодействует компонент FetchIt.
data-fetchit
Данный селектор добавляется элементу формы сниппетом. Он позволяет компоненту обрабатывать только нужные формы не нарушая поведения остальных форм на странице.
Сниппет во время загрузки чанка ищет форму и в случае отсутствия данного атрибута самостоятельно устанавливает его.
<form action="#" method="post" data-fetchit="*">
...
</form>
data-error
Указав в значении данного атрибута название поля, т.е. то же значение что в атрибуте name
вы указываете компоненту подгружать текст ошибки конкретного поля.
<input type="text" name="username" />
<span data-error="username"></span>
data-custom
По умолчанию FetchIt добавляет CSS классы из системной настройки fetchit.frontend.input.invalid.class
элементам полей ввода, но бывают случаи, когда из-за особенности вёрстки нужно добавлять их другим элементам, например элементам-обёрткам и для таких случаев есть данный селектор и системная настройка fetchit.frontend.custom.invalid.class
.
<div class="input-parent" data-custom="password">
<input type="password" name="password" />
</div>
JS API
Класс FetchIt
Данный класс отвечает за обработку форм и объявляется в скрипте, который идёт в составе компонента и который регистрируется в теге <head>
, но с атрибутом defer
, что позволяет откладывать его выполнение до тех пор, пока вся страница не будет загружена. Это позволяет скрипту не препятствовать загрузке страницы.
Свойства и методы класса
У класса есть несколько статических свойств. Важно! Не у экземпляра, а у самого класса.
FetchIt.forms
- Тип:
HTMLFormElement[]
Данное свойство хранит массив HTMLFormElement
всех обрабатываемых форм.
FetchIt.instances
- Тип:
Map
Данное свойство вернёт коллекцию Map
где храняться экземляры класса FetchIt
. Получить доступ к классу можно через форму.
- Пример:
const form = document.querySelector('#form');
const fetchit = FetchIt.instances.get(form);
FetchIt.Message
- Тип:
object
Данное свойство не объявлено, но все экземпляры класса будут пытаться вызвать её методы: before
, success
, error
, after
и reset
. Это сделано для удобства встраивания скрипта в вашу вёрстку. Т.е. вы можете объявить данное свойство в своем скрипте, например так:
FetchIt.Message = {
before() {
// Показать сообщение до отправки формы
},
success(message) {
// Показать сообщение в случае успешной отправки
},
error(message) {
// Показать сообщение в случае ошибки при отправке
},
after(message) {
// Показать сообщение в любом случае
},
reset() {
// Показать сообщение после сбрасывания данных формы
},
}
Как вы уже заметили, методы success
, error
и after
получают в качестве аргумента сообщение, которое будет возвращено вызываемым сниппетом.
FetchIt.sanitizeHTML()
- Тип:
function (str: string): string
Метод класса, который возвращает очищенную от HTML тегов строку переданную в качестве единственного аргумента.
FetchIt.create()
- Тип:
function (config: object): undefined
Фабричный метод, который создает экземляры класса FetchIt. Каждый экземпляр класса отвечает за свою форму.
FetchIt.events
- Тип:
object
Объект с событиями и их названиями. Может пригодиться при прототипном наследовании.
Доступ к классу
Надо помнить, для доступа к данному классу нужно дождаться выполнения скрипта в котором он объявлен.
Если у вас файловый скрипт, то достаточно указать атрибут defer
при его подключении (Напоминаем, что компонент регистрирует скрипт в теге <head>
).
А в случае с инлайновым скриптом, вам нужно дождаться выполнения скрипта, а это возможно в обработчике события DOMContentLoaded
. Пример:
document.addEventListener('DOMContentLoaded', () => {
console.log(FetchIt);
});
Экземпляр класса FetchIt
Ниже представлен список свойств и методов экземпляра класса FetchIt.
clearErrors()
Данный метод очищает все ошибки формы.
- Тип:
function (): undefined
- Пример:
document.addEventListener('fetchit:after', (e) => {
e.preventDefault();
const { fetchit } = e.detail;
fetchit.clearErrors();
});
clearError()
Данный метод очищает ошибки связанные с конкретным полем.
- Тип:
function (name: string): object
- Пример:
document.addEventListener('fetchit:after', (e) => {
e.preventDefault();
const { fetchit } = e.detail;
const { fields, errors, customErrors } = fetchit.clearError('password');
});
setError()
Данный метод устанавливает состояние невалидности конкретному полю по названию. Может быть удобным в случаях интеграции валидации на фронте.
- Тип:
function (name: string, message: string): undefined
- Пример:
document.addEventListener('fetchit:before', (e) => {
e.preventDefault();
const { fetchit } = e.detail;
// Валидация поля
fetchit.setError('email', 'Поле email не прошла валидацию');
});
Валидация на стороне клиента небезопасна и должна быть реализована только для удобства пользователя.
disableFields()
Данный метод устанавливает все элементы формы в состояние disabled
.
- Тип:
function (): undefined
enableFields()
Данный метод убирает состояние disabled
со всех элементов формы.
- Тип:
function (): undefined
getFields()
Данный метод возвращает массив полей по названию.
- Тип:
function (name: string): HTMLElement[]
События
Экземпляр класса генерирует события, которые могут быть полезны разработчикам. С их помощью вы сможете реализовать разные задачи и сценарии.
fetchit:before
- Аргументы:
(form <HTMLFormElement>, formData <FormData>, fetchit <FetchIt>)
Самое раннее событие, которое вызывается до отправки формы. Удобен в случаях, если вам нужно добавить поле и/или валидации данных на стороне клиента. При желании может прервать отправку формы.
document.addEventListener('fetchit:before', (e) => {
const { form, formData } = e.detail;
formData.set('newField', 'Значение нового поля'); // Добавляем новое поле
if (formData.get('name')?.length < 3) {
e.preventDefault(); // Отменяем отправку формы
}
})
fetchit:after
- Аргументы:
(form <HTMLFormElement>, formData <FormData>, response <object>, fetchit <FetchIt>)
Событие вызывается после отправки формы и получения ответа от сервера. Нужно для исключительных случаев, когда вам необходимо реализовать функционал вне зависимости от статуса ответа.
document.addEventListener('fetchit:after', (e) => {
const { response } = e.detail;
console.log(response.success); // true|false
console.log(response.message); // Сообщение от сервера
console.log(response.data); // Данные от сервера
})
fetchit:success
- Аргументы:
(form <HTMLFormElement>, formData <FormData>, response <object>, fetchit <FetchIt>)
Данное событие будет вызвано, если в ответе сервера поле success
будет true
и соответственно необходим для выполнения кода при успешной отправке формы.
fetchit:error
- Аргументы:
(form <HTMLFormElement>, formData <FormData>, response <object>, fetchit <FetchIt>)
Данное событие будет вызвано, если в ответе сервера поле success
будет false
и нужно для обработки ошибок.
fetchit:reset
- Аргументы:
(form <HTMLFormElement>, fetchit <FetchIt>)
Данное событие будет вызвано, при сброса формы. Может быть полезен в случаях когда необходимо скрыть кастомные сообщения или ошибки.
Больше примеров вы сможете найти ниже.
Примеры разметок форм
Разберем разметку формы для самого популярного фреймворка Bootstrap. А ниже вы найдете ссылки на разметки форм других менее популярных CSS фреймворков.
Допустим ваша вёрстка реализована на Bootstrap и у вас форма выглядит следующим образом:
<form class="row g-3">
<div class="col-md-4">
<label for="name" class="form-label">First name</label>
<input type="text" class="form-control" id="name" value="">
<div class="invalid-feedback"></div>
</div>
<div class="col-md-4">
<label for="email" class="form-label">Last name</label>
<input type="email" class="form-control" id="email" value="">
<div class="invalid-feedback"></div>
</div>
<div class="col-12">
<button class="btn btn-primary" type="submit">Submit form</button>
</div>
</form>
Валидаторы разметки до сих пор ругаются на пустой атрибут action, поэтому в нём необходимо указывать ссылку на страницу.
<form action="[[~[[*id]]]]" class="row g-3">
<div class="col-md-4">
<label for="name" class="form-label">First name</label>
<input type="text" class="form-control" id="name" name="name" value="[[+fi.name]]">
<div class="invalid-feedback" data-error="name">[[+fi.error.name]]</div>
</div>
<div class="col-md-4">
<label for="email" class="form-label">Last name</label>
<input type="email" class="form-control" id="email" name="email" value="[[+fi.email]]">
<div class="invalid-feedback" data-error="email">[[+fi.error.email]]</div>
</div>
<div class="col-12">
<button class="btn btn-primary" type="submit">Submit form</button>
</div>
</form>
Примеры разметок других фреймверков:
Примеры всплывающих сообщений
Хоть и FetchIt не имеет никаких зависимостей, всё подготовлено для того, чтобы с помощью минимального кода подключить любую готовую или вашу собственную библиотеку для показа сообщений.
С версии 1.1.0 и 3.1.0 доступна системная настройка
fetchit.frontend.default.notifier
при включении которой будут отображаться всплывающие уведомления Notyf.
- Notyf
- SweetAlert2
- iziToast
- Notiflix.Notify
- Notie
- Awesome Notifications
- Toastify JS
- AlertifyJS
- PNotify
- toastr
- jGrowl
- NOTY
Примеры модальных окон
В данном разделе представлен список примеров интеграции FetchIt с плагинами модальных окон.
Примеры дополнительной валидации
Список примеров интеграции FetchIt с популярными библиотеками валидации.
Смотрите также
Если вы в качестве обработчика используете сниппет Formit, вы можете использовать все его хуки и т.д.. Вот самое популярное:
- Сохранение форм в административной панели MODX
- Как использовать 1 обработчик для всех форм на сайте
- Как скрыть форму после успешной отправки.
- Перенаправление после успешной отправки на другую страницу
- Прикрепление файлов к форме
- Как в FORMit передать название и url адрес страницы
- Маска ввода для полей телефон, дата, время
- Обработка checkbox
- Базовая защита от спама — использование пустого поля NoSpam
- все стандартные хуки formit.
- Борьба со спамом: без капч и при помощи recaptcha от google.