Используя модуль bitrix webforms
создаём новую форму.
Создаём в папке шаблона два файла ajax.php
и ajax.js
:
ajax.php
<?php
require $_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/prolog_before.php';
// Подключаем модуль веб-форм
CModule::IncludeModule("form");
// Проверка валидности отправки формы
if (check_bitrix_sessid()) {
$formErrors = CForm::Check($_POST['WEB_FORM_ID'], $_REQUEST, false, "N", 'Y');
// Если не все обязательные поля заполнены
if (count($formErrors)) {
echo json_encode(['success' => false, 'errors' => $formErrors, 'data' => []]);
} elseif ($RESULT_ID = CFormResult::Add($_POST['WEB_FORM_ID'], $_REQUEST)) {
// Отправляем все события как в компоненте веб форм
CFormCRM::onResultAdded($_POST['WEB_FORM_ID'], $RESULT_ID);
CFormResult::SetEvent($RESULT_ID);
CFormResult::Mail($RESULT_ID);
// говорим что успешно заявку получили
echo json_encode(['success' => true, 'errors' => [], 'data' => ['result_id' => $RESULT_ID]]);
} else {
// Какие-то еще ошибки произошли
echo json_encode(['success' => false, 'errors' => $GLOBALS["strError"], 'data' => []]);
}
} else {
// Предотвратили CSRF атаку
echo json_encode(['success' => false, 'errors' => ['sessid' => 'Не верная сессия. Попробуйте обновить страницу'], 'data' => []]);
}
// Файл ниже подключать обязательно, там закрытие соединения с базой данных
require $_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/epilog_after.php';
ajax.js
/**
* Класс BAjax предназначен для отправки формы через AJAX запрос и обработки ответа в виде JSON.
*/
class BAjax {
static events = {
before: 'bajax:before',
success: 'bajax:success',
error: 'bajax:error',
after: 'bajax:after',
reset: 'bajax:reset',
}
/**
* Создает экземпляр класса BAjax.
* @param {Element} formElement - DOM элемент формы.
* @param {string} actionUrl - URL адрес, на который будет отправлен запрос.
*/
constructor(formElement, actionUrl) {
if (!(formElement instanceof HTMLFormElement)) {
throw new Error('Не форма');
}
this.form = formElement;
this.actionUrl = actionUrl || this.form.getAttribute('action');
this.form.addEventListener('submit', this.handleSubmit.bind(this));
this.form.addEventListener('reset', this.handleReset.bind(this));
}
/**
* Обработчик события отправки формы.
* @param {Event} event - Событие отправки формы.
*/
handleSubmit(event) {
event.preventDefault();
this.formData = new FormData(this.form);
this.clearErrors();
this.toggleDisabledForm(true);
const beforeEvent = new CustomEvent(BAjax.events.before, {
cancelable: true,
detail: {
core: this,
form: this.form,
formData: this.formData,
},
});
if (!document.dispatchEvent(beforeEvent)) {
this.finally();
return;
}
this.sendAjaxRequest(this.formData);
}
/**
* Отправляет AJAX запрос на сервер с данными формы.
* @param {FormData} formData - Данные формы.
*/
async sendAjaxRequest(formData) {
try {
const response = await fetch(this.actionUrl, { method: 'POST', body: formData });
if (!response.ok) {
this.showErrors({ error: response.statusText });
console.error(`Ошибка ${response.status}: ${response.statusText}`);
return;
}
const jsonData = await response.json();
if (!jsonData.success) {
this.showErrors(jsonData.errors);
return;
}
const successEvent = new CustomEvent(BAjax.events.success, {
detail: {
form: this.form,
formData: this.formData,
response: jsonData
},
});
if (!document.dispatchEvent(successEvent)) {
return;
}
} catch (error) {
this.showErrors({ error: error.message });
} finally {
this.finally();
}
}
/**
* Обработчик события сброса формы.
*/
handleReset() {
const resetEvent = new CustomEvent(BAjax.events.reset, {
detail: {
core: this,
form: this.form,
},
});
this.toggleDisabledForm(false);
document.dispatchEvent(resetEvent);
}
/**
* Вызывается после получения ответа на AJAX запрос.
*/
finally() {
const afterEvent = new CustomEvent(BAjax.events.after, {
detail: {
core: this,
form: this.form,
},
});
if (!document.dispatchEvent(afterEvent)) {
return;
}
this.handleReset();
}
/**
* Очистка ошибок с формы
*/
clearErrors() {
const listDataError = this.form.querySelectorAll('[data-error]');
listDataError.forEach((i) => (i.textContent = ''));
}
/**
* Отображает ошибки в форме.
* @param {Object} objErrors - Объект с ошибками.
*/
showErrors(objErrors = {}) {
this.clearErrors();
const errorEvent = new CustomEvent(BAjax.events.error, {
cancelable: true,
detail: {
core: this,
form: this.form,
formData: this.formData,
errors: objErrors,
},
});
if (!document.dispatchEvent(errorEvent)) {
return;
}
const $errorMsg = this.form.querySelector('.error-msg');
let errorStr = '';
for (let fieldKey in objErrors) {
const $dataError = this.form.querySelector(
`[data-error="${fieldKey}"]`
);
if ($dataError) {
$dataError.textContent = objErrors[fieldKey];
} else {
errorStr += objErrors[fieldKey] + '<br>';
}
}
if ($errorMsg) {
$errorMsg.innerHTML = errorStr;
} else {
console.error(objErrors);
}
}
/**
* Включает или отключает атрибут disabled для элементов формы.
* @param {boolean} toggle - Флаг для включения или отключения атрибута disabled.
*/
toggleDisabledForm(toggle) {
const elements = this.form.querySelectorAll(
':scope :not([data-bajax="no-disabled"])'
);
elements.forEach((element) => {
if (toggle) {
element.setAttribute('disabled', 'disabled');
} else {
element.removeAttribute('disabled');
}
});
}
}
В шаблон template.php
добавьте тег для вывода ошибок (обычно добавляется после кнопки отправки формы):
<div class="error-msg"></div>
В конец файла добавьте следующее:
template.php
<!-- ... -->
<script src="<?=$templateFolder?>/ajax.js"></script>
<script>
document.addEventListener("DOMContentLoaded", () => {
const formSender = new BAjax(document.getElementsByName('<?=$arResult['arForm']['SID']?>')[0], '<?=$templateFolder?>/ajax.php');
// Установка коллбэков (опционально)
document.addEventListener('bajax:before', (e) => {
const { form, formData } = e.detail;
console.log('Before send 🚀', form, formData);
// e.preventDefault(); // Отменяем отправку формы
});
document.addEventListener('bajax:after', () => {
console.log('After send 🎉');
});
document.addEventListener('bajax:success', (e) => {
const { response } = e.detail;
console.log('Success 🙌', response);
});
document.addEventListener('bajax:error', (e) => {
const { errors } = e.detail;
console.log('Error 😢', errors);
// errors - Объект с ошибками, ключ - название поля, значение - текст ошибки
});
document.addEventListener('bajax:reset', () => {
console.log('Form reset ❌');
});
});
</script>
Первая строка подключает ajax.js
файл с классом для отправки и обработки данных с формы. Далее добавляется код для вызова обработчика ajax
и добавление callback
событий.
Внутри шаблона можно добавить вывод ошибок рядом с полями в которых была ошибка. Для этого добавляем тег с атрибутом data-error
, значением которого является название поля:
<div data-error="name"></div>
При отправки всем элементам формы добавляется атрибут disabled
, если каким либо тегам не надо добавлять, то надо добавить в этот тег атрибут data-bajax="no-disabled"
.