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 с популярными библиотеками валидации.

Дополнительно

Добавлю еще пару примеров от себя, которые вам скорее всего пригодятся.

Кастомизация (изменение) внешнего вида всплывающих сообщений на Notyf

Например мне не нравится что сообщения вплывают в углу с права и довольно мелкие. К примеру хочется чтобы они были по центру, крупнее и с кнопкой закрыть, можно сделать это так.

  1. Добавьте кастомный CSS для увеличения размера: В вашем stylesheet (например, main.css) добавьте класс для уведомлений. Это сделает окошко крупнее (увеличьте значения по вкусу):
    .large-notyf {
      font-size: 25px; /* Крупный текст */
      padding: 0px 20px !important; /* Больше отступов */
      min-width: 400px; /* Минимальная ширина */
      border-radius: 10px !important; /* Опционально: скругление углов */
      background: #dae6ed; /* Опционально: цвет фона */
    }

    Если нужно, добавьте стили для конкретных типов (success/error), используя селекторы Notyf (например, .notyf__toast—success).

  2. Переопределите инициализацию Notyf в JS: Добавьте этот код в конец страницы или в отдельный JS-файл (после подключения Notyf и FetchIt). Мы создаём экземпляр Notyf с кастомными опциями (позиция по центру, класс для размера) и присваиваем его методам FetchIt.Message для обработки сообщений.
    document.addEventListener('DOMContentLoaded', () => {
      const notyf = new Notyf({
        position: {
          x: 'center',  // Горизонтально по центру
          y: 'center'   // Вертикально по центру (альтернатива: 'top' для верхнего центра)
        },
        duration: 5000,  // Длительность отображения (в мс, опционально)
        ripple: true,    // Эффект ripple (опционально)
        dismissible: true,  // Кнопка закрытия (опционально)
        types: [
          {
            type: 'success',
            className: 'large-notyf',  // Кастомный класс для размера
            background: 'green'  // Опционально: цвет
          },
          {
            type: 'error',
            className: 'large-notyf',  // Кастомный класс для размера
            background: 'red'  // Опционально: цвет
          }
        ]
      });
    
      // Присваиваем Notyf методам FetchIt для сообщений
      FetchIt.Message = {
        success(message) {
          notyf.success(message);
        },
        error(message) {
          notyf.error(message);
        },
      };
    });

На выходе получаем вот такую всплывашку:

Пример кастомного уведомления

Закрытие модальных окон, после отправки форм

В оф документации, есть 2 примера закрытия окно 1й от бутстрап 5 и 2й от Micromodal . Давайте еще разберем от пофигу какого компонента или своих кастомных окон. К примеру у на есть вот такое модальное окно:

<div class="form_modal">
    <div class="form__window">
        <div class="form__header">
            <div class="form__btn_close"></div> // кнопка закрытия
            <div class="form__title">Заголовок</div>
        </div>
        форма
    </div>
</div>

Закрыть его можно вот так:

document.addEventListener('fetchit:success', ({ detail: { form } }) => {
  // Находим контейнер модального окна относительно формы
  const modal = form.closest('.form_modal');
  if (!modal) return;

  // Находим кнопку закрытия
  const closeBtn = modal.querySelector('.form__btn_close');
  if (!closeBtn) return;

  // Имитируем клик по кнопке закрытия
  closeBtn.click();
});

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

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

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

 

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

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

Оцените автора
( 1 оценка, среднее 5 из 5 )
Web-Revenue.ru
Добавить комментарий

  1. Андрей

    Здравствуйте.
    Установил форму как у вас тут описано на своем сайте.
    Сообщение об отправке показывает, но на почту не приходит ничего.
    Не могли бы подсказать как решить проблему?

    Ответить
    1. Алексей автор

      Здравствуйте. Попробуйте добавить параметр emailFrom = (ваша почте, в идеале, это должна быть доменная почта (которая существует) — создайте ее на хостинге)

      Ответить
      1. Андрей

        Напишите мне, пожалуйста на емайл. Я бы хотел у вас заказать лендинг с формой.

        Ответить
        1. Алексей автор

          Написал вам на эту gmail почту.

          Ответить
  2. Даниил

    Здравствуйте, как добавить уведомление что YaSmartCaptcha не пройдена не могу найти. С этим компонентом /packages/utilities/yasmartcaptcha

    Ответить
    1. Алексей автор

      Здравствуйте. Не работал с YaSmartCaptcha, вообще капчи не использую, т.к. их не перевариваю)

      Ответить
  3. zoritoler imol

    Very interesting subject, appreciate it for posting.

    Ответить
    1. Алексей автор

      Please

      Ответить