FormLister: форма обратной связи

+ антиспам, ajaxSubmit, Bootstrap

Обычная форма обратной связи, которую я использую везде.

11.06.2018

В каждом-каждом проекте обязательно есть форма обратной связи. И вот этот кусочек функционала, который можно копипастить. Пара слов об особенностях именно этой реализации. Во-первых, это FormLister. Во-вторых, здесь есть набор правил для защиты от спама. Идея старая: запрещаем имени содержать латинские буквы и цифры, запрещаем в поле для сообщения вставлять все, что похоже на ссылку. В-третьих, предполагается использование Bootstrap, поэтому все классы заточены под него. В-четвертых, используется ajaxSubmit для отправки формы из всплывающего окошка.

Вот чанк (или часть шаблона, если вы - редиска), где описано всплывающее окошко, обычное для Bootstrap, и вызов сниппетов ajaxSubmit и FormLister.

<!-- Modal -->
<div class="modal fade" id="feedbackModal" tabindex="-1" aria-labelledby="feedbackModalLabel" aria-hidden="true">
	<div class="modal-dialog" role="document">
		<div class="modal-content">
			<div class="modal-header">
				<div class="modal-title" id="feedbackModalLabel">Записаться на консультацию</div>
				<button type="button" class="close" data-dismiss="modal" aria-label="Close">
					<span aria-hidden="true">&times;</span>
				</button>
			</div>
			<div class="modal-body">
				[[ajaxSubmit? &id=`feedback` &noJQuery=`1` &form=`#feedbackForm` &container=`#feedbackDiv`]]
				<div id="feedbackDiv">
					[[FormLister?
					&formid=`feedbackForm`
					&formTpl=`feedbackTpl`
					&formControls=`agreeCheck`
					&emptyFormControls=`{"sendCheck":"0"}`
					&successTpl=`feedbackSuccess`
					&errorTpl=`@CODE: <div class="invalid-feedback">[+message+]</div>`
					&requiredClass=`is-invalid`
					&errorClass=`is-invalid`
					&to=`[(email_mngr)]`
					&subject=`Запись на консультацию`
					&reportTpl=`feedbackReport`
					&ccSender=`1`
					&parseMailerParams=`1`
					&replyTo=`@CODE:[+email.value+]`
					&autoSubject=`Запись на консультацию`
					&ccSenderTpl=`feedbackCcReport`
					&rules=`{
						"name":{
							"required":"Пожалуйста, представьтесь.",
							"matches":{
								"params":"/^[^a-zA-Z0-9]+$/u",
								"message":"Пожалуйста, представьтесь на русском языке."
							}
						},
						"phone":{
							"required":"Пожалуйста, укажите свой номер телефона для связи с вами.",
							"phone":"Проверьте, пожалуйста, указанный номер телефона."
						},
						"!email":{
							"email":"Проверьте, пожалуйста, указанный адрес электронной почты."
						},
						"!comment":{
							"matches":{
								"params":"/^(?:(?!\\w+\\.\\w+).)*$/",
								"message":"Здесь нельзя отправлять ссылки. Извините за неудобства."
							}
						}
					}`				
					]]
				</div>
			</div>
		</div>
	</div>
</div>

Коротко опишу, что здесь происходит. ajaxSubmit вставляет скрипт, который слушает submit формы #feedbackForm в контейнере #feedbackDiv. На случай, если где-то будет использоваться ещё один ajaxSubmit, для этого устанавливаем уникальный идентификатор в параметре &id - этот идентификатор будет включён в названия функций скрипта. Помимо прочего, запрещаем этому сниппету включать свою древнюю jQuery, поскольку эта библиотека у меня уже подключена к этому моменту. Кстати, сам скрипт, который вставляется сниппетом ajaxSubmit, требует напильничка для совместимости с более свежими версиями jQuery. Вот тут показано, как обновить ajaxSubmit.

Теперь о параметрах FormLister.

&formid=`feedbackForm` - в самой форме нужно скрытое поле formid с этим же значение, + я ставлю такой же id для самой формы, и этот же id я использовала для подключения AjaxSubmit.
&formTpl=`feedbackTpl` - чанк с формой.
&formControls=`agreeCheck` - список имен полей-чекбоксов и радиобаттонов, которые должны быть установлены.
&emptyFormControls=`{"sendCheck":"0"}` - список имен полей-чекбоксов и радиобаттонов, которые есть в форме, могут быть установлены пользователем, а могут быть проигнорированы.
&successTpl=`feedbackSuccess` - чанк с сообщением пользователю об успешной отправке данных.
&errorTpl=`@CODE: <div class="invalid-feedback">[+message+]</div>` - код вывода общего сообщения об ошибках.
&requiredClass=`is-invalid` - класс ошибки для полей, которые обязательны к заполнению, но не заполнены.
&errorClass=`is-invalid` - класс ошибки для полей, в которые введены некорректные данные.
&to=`[(email_mngr)]` - здесь адрес администратора сайта. Я разделяю адрес отправителя по умолчанию и адрес администратора сайта. Почту админа храню в конфиге сайта, записываю ее туда с помощью плагина customSettings.
&subject=`Запись на консультацию` - тема письма для админа сайта.
&reportTpl=`feedbackReport` - чанк с текстом письма админу.
&ccSender=`1` - нужно отправлять письмо автоответчика пользователю.
&parseMailerParams=`1` - разрешаем парсить отправленную форму на предмет почты пользователя - туда отправит своё письмо автоответчик.
&replyTo=`@CODE:[+email.value+]` - нужно отправлять письмо пользователю на его электронку, она живет в плейсхолдере [+email.value+].
&autoSubject=`Запись на консультацию` - тема письма автоответчика. Выделю этот параметр отдельно, поскольку обычно темы отличаются формулировкой.
&ccSenderTpl=`feedbackCcReport` - чанк с текстом письма автоответчика.
&rules - правила проверки данных в полях формы, далее подробнее:

"name":{
	"required":"Пожалуйста, представьтесь.",
	"matches":{
		"params":"/^[^a-zA-Z0-9]+$/u",
		"message":"Пожалуйста, представьтесь на русском языке."
	}
},

Поле для ввода имени обязательно к заполнению, и туда нельзя вписать латинские буквы или цифры.

"phone":{
	"required":"Пожалуйста, укажите свой номер телефона для связи с вами.",
	"phone":"Проверьте, пожалуйста, указанный номер телефона."
},

Поле для ввода номера телефона обязательно к заполнению, и данные обязаны быть номером телефона.

"!email":{
	"email":"Проверьте, пожалуйста, указанный адрес электронной почты."
},

Адрес электронки для примера не обязателен, проверка данных в поле проходит, только если это поле заполнено. И данные в этом поле обязаны быть адресом электронной почты.

"!comment":{
	"matches":{
		"params":"/^(?:(?!\\w+\\.\\w+).)*$/",
		"message":"Здесь нельзя отправлять ссылки. Извините за неудобства."
	}
}

Поле для ввода сообщения не обязательно к заполнению, проверяется, если заполнено. Формально регулярное выражение проверяет отсутствие ссылок в этом поле. Фактически - запрещены конструкции с точкой. Например, фраза "Яндекс.Директ" тоже не пройдет эту проверку.

Теперь посмотрим на чанк с формой.

<form id="feedbackForm" method="post">
	<input type="hidden" name="formid" value="feedbackForm"/>
	<div class="form-group">
		<label for="inputName">Имя *</label>
		<input type="text" class="form-control [+name.classname+]" id="inputName" name="name" required="required" value="[+name+]">
		[+name.error+]
	</div>
	<div class="form-row mb-3">
		<div class="form-group col-md-6 mb-0">
			<label for="inputPhone">Телефон *</label>
			<input type="phone" class="form-control [+phone.classname+]" id="inputPhone" name="phone" required="required" aria-describedby="phoneHelpBlock" value="[+phone+]">
			[+phone.error+]
		</div>
		<div class="form-group col-md-6 mb-0">
			<label for="inputEmail">Email</label>
			<input type="email" class="form-control [+email.classname+]" id="inputEmail" name="email" value="[+email+]">
			[+email.error+]
		</div>
		<small id="phoneHelpBlock" class="form-text text-muted">Мы никому не передаем ваши контактные данные. Также мы не отправим вам рассылку, если вы не согласны ее получать. Ваш телефон и электронная почта используются только для связи с вами.</small>
	</div>
	<div class="form-group">
		<label for="inputMessage">Пояснение</label>
		<textarea class="form-control [+comment.classname+]" id="inputMessage" name="comment" aria-describedby="messageHelpBlock">[+comment+]</textarea>
		[+comment.error+]
		<small id="messageHelpBlock" class="form-text text-muted">Укажите здесь ваш вопрос и другие детали, которые вы считаете важными.</small>
	</div>
	<div class="form-group">
		<div class="form-check">
			<input class="form-check-input [+agreeCheck.classname+]" type="checkbox" id="agreeCheck" required="required" value="1" name="agreeCheck">
			<label class="form-check-label" for="agreeCheck">
				Я принимаю <a href="..." target="_blank">Пользовательское соглашение</a> и согласен с <a href="..." target="_blank">Политикой конфиденциальности</a>.
			</label>
			[+agreeCheck.error+]
		</div>
	</div>
	<div class="form-group">
		<div class="form-check">
			<input class="form-check-input [+sendCheck.classname+]" type="checkbox" id="sendCheck" value="1" name="sendCheck">
			<label class="form-check-label" for="sendCheck">
				Я согласен получать информационную рассылку для клиентов.
			</label>
			[+sendCheck.error+]
		</div>
	</div>
	<button type="submit" class="btn btn-primary">Отправить</button>
</form>

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

<a class="btn btn-primary" href="#" role="button" data-toggle="modal" data-target="#feedbackModal">Обратная связь</a>

Ну и закончу на этом.