В предыдущих уроках мы собрали красивые страницы, но формы на них — это просто «мертвый» HTML. Если нажать кнопку, страница просто перезагрузится, и ничего не произойдет.
В этом уроке мы превратим их в полноценный рабочий инструмент. Мы сделаем так, чтобы заявки улетали на почту мгновенно, без перезагрузки страницы (AJAX), а пользователь видел красивые уведомления в стиле нашего шаблона.
Что нам понадобится:
- FormIt — «Мозг» (Backend). Он принимает данные, проверяет их (валидация) и отправляет письма.
- FetchIt — «Курьер» (Frontend). Он отправляет данные из браузера на сервер без перезагрузки и принимает ответ (успех или ошибку).
Важно: убедитесь, что оба компонента установлены через «Установщик пакетов».
Подготовка: универсальный шаблон письма
Прежде чем делать формы, создадим единый шаблон письма, который будет приходить администратору. Мы сделаем его универсальным, чтобы он подходил для аудита и контактов.
Создайте файл чанка по пути: chunks/fetchIt/form.tpl.
Код чанка chunks/fetchIt/form.tpl:
<h3>Новая заявка с сайта</h3>
{if $name?}
<p><strong>Имя:</strong> {$name}</p>
{/if}
{if $phone?}
<p><strong>Телефон:</strong> {$phone}</p>
{/if}
{if $email?}
<p><strong>Email:</strong> <a href="mailto:{$email}">{$email}</a></p>
{/if}
{if $url?}
<p><strong>URL сайта (Аудит):</strong> <a href="{$url}" target="_blank">{$url}</a></p>
{/if}
{if $message?}
<hr>
<p><strong>Сообщение:</strong><br>
{$message|nl2br}</p>
{/if}
Здесь мы проверяем наличие полей: если поле заполнено пользователем, оно добавится в письмо.
Часть 1: Форма «SEO Audit» (Простая)
Эта форма встречается у нас в блоке pb.seo_audit (Главная, Услуги) и в подвале сайта.
1. Создаем файл формы
Создайте файл: chunks/fetchIt/form_audit.tpl. Мы берем HTML из верстки и добавляем атрибуты для валидации и вывода ошибок.
Код чанка chunks/fetchIt/form_audit.tpl:
<form action="{$_modx->resource.id | url}" method="post" class="custom-form-style-1 form-errors-light" id="seoAuditForm">
<div class="row mb-4">
<div class="form-group col-md-6 pe-md-2">
<input type="text" class="form-control {if $errors['url']}is-invalid{/if}" name="url" value="{$url}" placeholder="Enter URL" required />
{if $errors['url']}<div class="invalid-feedback">{$errors['url']}</div>{/if}
</div>
<div class="form-group col-md-6 ps-md-2">
<input type="email" class="form-control {if $errors['email']}is-invalid{/if}" name="email" value="{$email}" placeholder="Enter E-mail Address" required />
{if $errors['email']}<div class="invalid-feedback">{$errors['email']}</div>{/if}
</div>
</div>
<div class="row justify-content-center">
<div class="form-group col-auto mb-0">
{* Кнопка отправки *}
<button type="submit" class="btn btn-quaternary btn-rounded font-weight-bold px-5 py-3 text-3">CHECK NOW</button>
</div>
</div>
{* Сообщение об успехе (показывается через JS) *}
<div class="alert alert-success d-none mt-4" id="seoSuccessMessage">
Ваша заявка на аудит принята!
</div>
</form>
2. Подключаем в PageBlock pb.seo_audit
Открываем чанк блока pb.seo_audit и заменяем старую верстку формы на вызов сниппета FetchIt.
Код вызова:
{'!FetchIt' | snippet : [
'snippet' => 'FormIt',
'hooks' => 'email',
'validate' => 'url:required,email:required:email',
'form' => '@FILE chunks/fetchIt/form_audit.tpl',
'emailTpl' => '@FILE chunks/fetchIt/form.tpl',
'emailTo' => $_modx->config.emailsender,
'emailFrom' => 'noreply@site.com',
'emailSubject' => 'Заявка на SEO Аудит',
'successMessage' => 'Заявка успешно отправлена!'
]}
Важно:
- Мы используем
@FILE, чтобы загружать чанк напрямую из файла. emailToберет почту из системных настроек.emailFrom(от кого) лучше указывать на домене сайта (например,noreply@yoursite.com), чтобы письма не попадали в спам.- Не забудьте также заменить форму на этот вызов в чанке подвала
pb.footer_form!
Часть 2: Контактная форма (Сложная)
Переходим к странице Контактов. Там у нас был блок pb.contacts, содержащий большую форму с сообщением.
1. Создаем файл формы
Создайте файл: chunks/fetchIt/form_contacts.tpl.
Код чанка chunks/fetchIt/form_contacts.tpl:
<form class="contact-form custom-form-style-1" action="{$_modx->resource.id | url}" method="post" id="contactForm">
{* Блок успеха (скрыт по умолчанию, покажем через JS) *}
<div class="contact-form-success alert alert-success d-none mt-4">
<strong>Success!</strong> Ваше сообщение успешно отправлено.
</div>
{* Блок ошибки (FormIt) *}
<div class="contact-form-error alert alert-danger mt-4 {if !$errors}d-none{/if}">
<strong>Error!</strong> Проверьте правильность заполнения полей.
</div>
<div class="row">
<div class="form-group col mb-3">
<input type="text" name="name" value="{$name}" class="form-control custom-bg-color-light-1 border-0 {if $errors['name']}is-invalid{/if}" placeholder="Your Name" required>
{if $errors['name']}<span class="text-danger small">{$errors['name']}</span>{/if}
</div>
</div>
<div class="row">
<div class="form-group col mb-3">
<input type="email" name="email" value="{$email}" class="form-control custom-bg-color-light-1 border-0 {if $errors['email']}is-invalid{/if}" placeholder="E-mail Address" required>
{if $errors['email']}<span class="text-danger small">{$errors['email']}</span>{/if}
</div>
</div>
<div class="row">
<div class="form-group col mb-3">
<textarea rows="6" name="message" class="form-control custom-bg-color-light-1 border-0 {if $errors['message']}is-invalid{/if}" placeholder="Your Message" required>{$message}</textarea>
{if $errors['message']}<span class="text-danger small">{$errors['message']}</span>{/if}
</div>
</div>
<div class="row">
<div class="form-group col mb-3">
<button type="submit" class="btn btn-gradient btn-rounded font-weight-bold px-5 py-3 text-3">SEND NOW</button>
</div>
</div>
</form>
2. Обновляем PageBlock pb.contacts
В чанке блока pb.contacts находим правую колонку с формой и вставляем туда вызов FetchIt:
{* ... Левая колонка с контактами ... *}
{* ПРАВАЯ КОЛОНКА: ФОРМА *}
<div class="col-md-10 col-lg-6 appear-animation" data-appear-animation="fadeInRightShorter" data-appear-animation-delay="2000">
<div class="card border-0 custom-border-radius-1 box-shadow-1 p-2">
<div class="card-body p-4 z-index-1">
<h4 class="text-color-dark font-weight-semibold text-5 line-height-3 ls-0 mb-1 mt-2 pe-5 me-5">{$form_title}</h4>
<p class="pb-2 mb-4">{$form_text}</p>
{* ВЫЗОВ FETCHIT *}
{'!FetchIt' | snippet : [
'snippet' => 'FormIt',
'form' => '@FILE chunks/fetchIt/form_contacts.tpl',
'emailTpl' => '@FILE chunks/fetchIt/form.tpl',
'hooks' => 'email',
'emailTo' => $_modx->config.emailsender,
'emailFrom' => 'noreply@site.com',
'emailSubject' => 'Контактная форма с сайта',
'validate' => 'name:required,email:required:email,message:required'
]}
</div>
</div>
</div>
Часть 3: Обработка ответов (JS)
FetchIt по умолчанию использует библиотеку Notyf (всплывающие уведомления). Это удобно, но в шаблоне Porto есть свои красивые alert блоки (зеленые плашки), которые мы сохранили в HTML (.contact-form-success).
Давайте научим FetchIt показывать именно их. Создайте файл assets/js/fetchit_custom.js (и подключите его в base.tpl после скриптов темы) или добавьте этот код в ваш custom.js:
document.addEventListener('DOMContentLoaded', () => {
// 1. Слушаем событие успешной отправки формы
document.addEventListener('fetchit:success', (e) => {
const { form, response } = e.detail;
// Логика для Контактной формы
if (form.id === 'contactForm') {
// Скрываем форму (опционально) или просто очищаем
form.reset();
// Показываем блок .contact-form-success
const successAlert = form.querySelector('.contact-form-success');
if (successAlert) {
successAlert.classList.remove('d-none');
successAlert.classList.add('d-block');
}
// Скрываем блок ошибок, если он виден
const errorAlert = form.querySelector('.contact-form-error');
if (errorAlert) errorAlert.classList.add('d-none');
}
// Логика для SEO Аудита
if (form.id === 'seoAuditForm') {
const successMsg = form.querySelector('#seoSuccessMessage');
if (successMsg) successMsg.classList.remove('d-none');
form.reset();
}
});
// 2. Слушаем событие ошибки (если хотим показать общий алерт ошибки)
document.addEventListener('fetchit:error', (e) => {
const { form } = e.detail;
if (form.id === 'contactForm') {
const errorAlert = form.querySelector('.contact-form-error');
if (errorAlert) {
errorAlert.classList.remove('d-none');
errorAlert.classList.add('d-block');
}
}
});
});
Заключение
Теперь ваши блоки в PageBlocks не просто «мертвая верстка», а полноценные функциональные элементы.
Что мы сделали:
- Вынесли HTML форм в отдельные файлы чанков.
- Создали универсальный шаблон письма.
- Заменили верстку в блоках PageBlocks на вызов
{'!FetchIt' | snippet : ...}с правильными путями через@FILE. - Дописали JS-скрипт, чтобы интегрировать ответы сервера в дизайн шаблона Porto.
В следующем уроке мы займемся созданием раздела Блог, где научимся работать с коллекциями ресурсов и пагинацией.







