FetchIt: отправка MODX форм с помощью Fetch API

FetchIt: отправка MODX форм с помощью Fetch API MODX Revo

Разберем довольно свежее 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">
fenom
<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>
Fenom

<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>
Fenom

<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>`
]]
Fenom

{'!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

Данное свойство не объявлено, но все экземпляры класса будут пытаться вызвать её методы: beforesuccesserrorafter и reset. Это сделано для удобства встраивания скрипта в вашу вёрстку. Т.е. вы можете объявить данное свойство в своем скрипте, например так:

FetchIt.Message = {
  before() {
    // Показать сообщение до отправки формы
  },
  success(message) {
    // Показать сообщение в случае успешной отправки
  },
  error(message) {
    // Показать сообщение в случае ошибки при отправке
  },
  after(message) {
    // Показать сообщение в любом случае
  },
  reset() {
    // Показать сообщение после сбрасывания данных формы
  },
}

Как вы уже заметили, методы successerror и 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.

Примеры модальных окон

В данном разделе представлен список примеров интеграции FetchIt с плагинами модальных окон.

Примеры дополнительной валидации

Список примеров интеграции FetchIt с популярными библиотеками валидации.

Смотрите также

Если вы в качестве обработчика используете сниппет Formit, вы можете использовать все его хуки и т.д.. Вот самое популярное:

  1. Сохранение форм в административной панели MODX
  2. Как использовать 1 обработчик для всех форм на сайте
  3. Как скрыть форму после успешной отправки.
  4. Перенаправление после успешной отправки на другую страницу
  5. Прикрепление файлов к форме
  6. Как в FORMit передать название и url адрес страницы
  7. Маска ввода для полей телефон, дата, время
  8. Обработка checkbox
  9. Базовая защита от спама — использование пустого поля NoSpam
  10. все стандартные хуки formit.
  11. Борьба со спамом: без капч и при помощи recaptcha от google.

 

Поделиться с друзьями
Алексей

Веб-дизайнер и SEO оптимизатор. Занимаюсь созданием сайтов с 2010 года и их продвижение с 2012 года!

Оцените автора
( Пока оценок нет )
Web-Revenue.ru
Добавить комментарий