Skip to content

Instantly share code, notes, and snippets.

@WengerK
Last active June 12, 2023 19:39
Show Gist options
  • Save WengerK/aac6486b95c120e683cac4be6e1727d6 to your computer and use it in GitHub Desktop.
Save WengerK/aac6486b95c120e683cac4be6e1727d6 to your computer and use it in GitHub Desktop.
Drupal 8 — Inline validation in forms

Article Ressources - Drupal 8 — Inline validation in forms

This is the Gist repository for my article Drupal 8 — Inline validation in forms.

Be aware that this article has been wrote for the Blog of Antistatique — Web Agency in Lausanne, Switzerland. A place where I work as Full Stack Web Developer.

Feel free to read it the full article on Medium or check it out on Antistatique.

Content of this Gist :

  • ContactForm-basic.php : Basic form whitout validation
  • ContactForm-validations.php : Complete form using standard Drupal 8 validations
  • ContactForm-validations-inline.php : Complete form using inline validations
  • InlineErrorFormTrait.php : Apply all errors as inline field error
<?php
/**
* @file
* Contains \Drupal\my_contact\Form\ContactForm.
*/
namespace Drupal\my_contact\Form;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
// Traits
use Drupal\Core\StringTranslation\StringTranslationTrait;
class ContactForm extends FormBase {
use StringTranslationTrait;
/**
* {@inheritdoc}.
*/
public function getFormId() {
return 'my_contact_form';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state, $params = NULL) {
// This will generate an anchor scroll to the form when submitting
$form['#action'] = '#my-contact-form';
$form['personnal'] = array(
'#type' => 'fieldset',
'#title' => $this->t('Your personnal data'),
);
$form['personnal']['firstname'] = array(
'#title' => $this->t('Your firstname *'),
'#placeholder' => $this->t('Alain'),
'#type' => 'textfield',
'#attributes' => ['size' => 25],
'#required' => false,
'#prefix' => '<div class="form-group">',
'#suffix' => '</div>',
);
$form['personnal']['lastname'] = array(
'#title' => $this->t('Your lastname *'),
'#placeholder' => $this->t('Rochat'),
'#type' => 'textfield',
'#attributes' => ['size' => 24],
'#required' => false,
'#prefix' => '<div class="form-group">',
'#suffix' => '</div>',
);
$form['personnal']['email'] = array(
'#title' => $this->t('Your email *'),
'#placeholder' => $this->t('alain.rochat@domain.ltd'),
'#type' => 'textfield',
'#required' => false,
'#prefix' => '<div class="form-group">',
'#suffix' => '</div>',
);
$form['message'] = array(
'#type' => 'fieldset',
'#title' => $this->t('Your message'),
);
$form['message']['subject'] = array(
'#title' => $this->t('Subject *'),
'#type' => 'textfield',
'#required' => false,
'#prefix' => '<div class="form-group">',
'#suffix' => '</div>',
);
$form['message']['message'] = array(
'#title' => $this->t('Message *'),
'#type' => 'textarea',
'#required' => false,
'#attributes' => ['cols' => 59],
'#prefix' => '<div class="form-group">',
'#suffix' => '</div>',
);
$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => $this->t('Send'),
'#attributes' => ['class' => array('btn-lg btn-primary pull-right')],
'#button_type' => 'primary',
'#prefix' => '<div class="form-group">',
'#suffix' => '</div>',
);
return $form;
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$data = array(
'firstname' => $form_state->getValue('firstname'),
'lastname' => $form_state->getValue('lastname'),
'email' => $form_state->getValue('email'),
'subject' => $form_state->getValue('subject'),
'message' => $form_state->getValue('message'),
);
drupal_set_message($this->t('Thank you very much @firstname @lastname for your message. You will receive a confirmation email shortly.', [
'@firstname' => $form_state->getValue('firstname'),
'@lastname' => $form_state->getValue('lastname'),
]));
}
}
<?php
/**
* @file
* Contains \Drupal\my_contact\Form\ContactForm.
*/
namespace Drupal\my_contact\Form;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
// Traits
use Drupal\Core\StringTranslation\StringTranslationTrait;
class ContactForm extends FormBase {
use StringTranslationTrait;
/**
* {@inheritdoc}.
*/
public function getFormId() {
return 'my_contact_form';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state, $params = NULL) {
// This will generate an anchor scroll to the form when submitting
$form['#action'] = '#my-contact-form';
$form['personnal'] = array(
'#type' => 'fieldset',
'#title' => $this->t('Your personnal data'),
);
$form['personnal']['firstname'] = array(
'#title' => $this->t('Your firstname *'),
'#placeholder' => $this->t('Alain'),
'#type' => 'textfield',
'#attributes' => ['size' => 25],
'#required' => false,
'#prefix' => '<div class="form-group">',
'#suffix' => '</div>',
);
$form['personnal']['lastname'] = array(
'#title' => $this->t('Your lastname *'),
'#placeholder' => $this->t('Rochat'),
'#type' => 'textfield',
'#attributes' => ['size' => 24],
'#required' => false,
'#prefix' => '<div class="form-group">',
'#suffix' => '</div>',
);
$form['personnal']['email'] = array(
'#title' => $this->t('Your email *'),
'#placeholder' => $this->t('alain.rochat@domain.ltd'),
'#type' => 'textfield',
'#required' => false,
'#prefix' => '<div class="form-group">',
'#suffix' => '</div>',
);
$form['message'] = array(
'#type' => 'fieldset',
'#title' => $this->t('Your message'),
);
$form['message']['subject'] = array(
'#title' => $this->t('Subject *'),
'#type' => 'textfield',
'#required' => false,
'#prefix' => '<div class="form-group">',
'#suffix' => '</div>',
);
$form['message']['message'] = array(
'#title' => $this->t('Message *'),
'#type' => 'textarea',
'#required' => false,
'#attributes' => ['cols' => 59],
'#prefix' => '<div class="form-group">',
'#suffix' => '</div>',
);
$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => $this->t('Send'),
'#attributes' => ['class' => array('btn-lg btn-primary pull-right')],
'#button_type' => 'primary',
'#prefix' => '<div class="form-group">',
'#suffix' => '</div>',
);
return $form;
}
/**
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
// Assert the firstname is valid
if (!$form_state->getValue('firstname') || empty($form_state->getValue('firstname'))) {
$form_state->setErrorByName('firstname', $this->t('Votre prénom est obligatoire.'));
}
// Assert the lastname is valid
if (!$form_state->getValue('lastname') || empty($form_state->getValue('lastname'))) {
$form_state->setErrorByName('lastname', $this->t('Votre nom est obligatoire.'));
}
// Assert the email is valid
if (!$form_state->getValue('email') || !filter_var($form_state->getValue('email'), FILTER_VALIDATE_EMAIL)) {
$form_state->setErrorByName('email', $this->t('Votre adresse e-mail semble invalide.'));
}
// Assert the subject is valid
if (!$form_state->getValue('subject') || empty($form_state->getValue('subject'))) {
$form_state->setErrorByName('subject', $this->t('Le sujet de votre demande est important.'));
}
// Assert the message is valid
if (!$form_state->getValue('message') || empty($form_state->getValue('message'))) {
$form_state->setErrorByName('message', $this->t('Le message de votre demande est important.'));
}
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$data = array(
'firstname' => $form_state->getValue('firstname'),
'lastname' => $form_state->getValue('lastname'),
'email' => $form_state->getValue('email'),
'subject' => $form_state->getValue('subject'),
'message' => $form_state->getValue('message'),
);
drupal_set_message($this->t('Thank you very much @firstname @lastname for your message. You will receive a confirmation email shortly.', [
'@firstname' => $form_state->getValue('firstname'),
'@lastname' => $form_state->getValue('lastname'),
]));
}
}
<?php
/**
* @file
* Contains \Drupal\my_contact\Form\ContactForm.
*/
namespace Drupal\my_contact\Form;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Symfony\Component\PropertyAccess\PropertyAccess;
// Traits
use Drupal\Core\StringTranslation\StringTranslationTrait;
class ContactForm extends FormBase {
use StringTranslationTrait;
/**
* {@inheritdoc}.
*/
public function getFormId() {
return 'my_contact_form';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state, $params = NULL) {
// This will generate an anchor scroll to the form when submitting
$form['#action'] = '#my-contact-form';
// Disable caching & HTML5 validation
$form['#cache']['max-age'] = 0;
$form['#attributes']['novalidate'] = 'novalidate';
$form['personnal'] = array(
'#type' => 'fieldset',
'#title' => $this->t('Your personnal data'),
);
$form['personnal']['firstname'] = array(
'#title' => $this->t('Your firstname *'),
'#placeholder' => $this->t('Alain'),
'#type' => 'textfield',
'#attributes' => ['size' => 25],
'#required' => false,
'#prefix' => '<div class="form-group">',
'#suffix' => '</div>',
);
$form['personnal']['lastname'] = array(
'#title' => $this->t('Your lastname *'),
'#placeholder' => $this->t('Rochat'),
'#type' => 'textfield',
'#attributes' => ['size' => 24],
'#required' => false,
'#prefix' => '<div class="form-group">',
'#suffix' => '</div>',
);
$form['personnal']['email'] = array(
'#title' => $this->t('Your email *'),
'#placeholder' => $this->t('alain.rochat@domain.ltd'),
'#type' => 'textfield',
'#required' => false,
'#prefix' => '<div class="form-group">',
'#suffix' => '</div>',
);
$form['message'] = array(
'#type' => 'fieldset',
'#title' => $this->t('Your message'),
);
$form['message']['subject'] = array(
'#title' => $this->t('Subject *'),
'#type' => 'textfield',
'#required' => false,
'#prefix' => '<div class="form-group">',
'#suffix' => '</div>',
);
$form['message']['message'] = array(
'#title' => $this->t('Message *'),
'#type' => 'textarea',
'#required' => false,
'#attributes' => ['cols' => 59],
'#prefix' => '<div class="form-group">',
'#suffix' => '</div>',
);
$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => $this->t('Send'),
'#attributes' => ['class' => array('btn-lg btn-primary pull-right')],
'#button_type' => 'primary',
'#prefix' => '<div class="form-group">',
'#suffix' => '</div>',
);
return $form;
}
/**
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
// Assert the firstname is valid
if (!$form_state->getValue('firstname') || empty($form_state->getValue('firstname'))) {
$form_state->setErrorByName('[personnal][firstname]', $this->t('Votre prénom est obligatoire.'));
}
// Assert the lastname is valid
if (!$form_state->getValue('lastname') || empty($form_state->getValue('lastname'))) {
$form_state->setErrorByName('[personnal][lastname]', $this->t('Votre nom est obligatoire.'));
}
// Assert the email is valid
if (!$form_state->getValue('email') || !filter_var($form_state->getValue('email'), FILTER_VALIDATE_EMAIL)) {
$form_state->setErrorByName('[personnal][email]', $this->t('Votre adresse e-mail semble invalide.'));
}
// Assert the subject is valid
if (!$form_state->getValue('subject') || empty($form_state->getValue('subject'))) {
$form_state->setErrorByName('[message][subject]', $this->t('Le sujet de votre demande est important.'));
}
// Assert the message is valid
if (!$form_state->getValue('message') || empty($form_state->getValue('message'))) {
$form_state->setErrorByName('[message][message]', $this->t('Le message de votre demande est important.'));
}
// If validation errors, add inline errors
if ($errors = $form_state->getErrors()) {
// Add error to fields using Symfony Accessor
$accessor = PropertyAccess::createPropertyAccessor();
foreach ($errors as $field => $error) {
if ($accessor->getValue($form, $field)) {
$accessor->setValue($form, $field.'[#prefix]', '<div class="form-group error">');
$accessor->setValue($form, $field.'[#suffix]', '<div class="input-error-desc">' .$error. '</div></div>');
}
}
}
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$data = array(
'firstname' => $form_state->getValue('firstname'),
'lastname' => $form_state->getValue('lastname'),
'email' => $form_state->getValue('email'),
'subject' => $form_state->getValue('subject'),
'message' => $form_state->getValue('message'),
);
drupal_set_message($this->t('Thank you very much @firstname @lastname for your message. You will receive a confirmation email shortly.', [
'@firstname' => $form_state->getValue('firstname'),
'@lastname' => $form_state->getValue('lastname'),
]));
}
}
<?php
/**
* @file - PARTIAL
* Contains \Drupal\my_contact\Form\ContactForm.
*/
...
use Symfony\Component\PropertyAccess\PropertyAccess;
...
/**
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
// Assert the firstname is valid
if (!$form_state->getValue('firstname') || empty($form_state->getValue('firstname'))) {
$form_state->setErrorByName('[personnal][firstname]', $this->t('Votre prénom est obligatoire.'));
}
// Assert the lastname is valid
if (!$form_state->getValue('lastname') || empty($form_state->getValue('lastname'))) {
$form_state->setErrorByName('[personnal][lastname]', $this->t('Votre nom est obligatoire.'));
}
// Assert the email is valid
if (!$form_state->getValue('email') || !filter_var($form_state->getValue('email'), FILTER_VALIDATE_EMAIL)) {
$form_state->setErrorByName('[personnal][email]', $this->t('Votre adresse e-mail semble invalide.'));
}
// If validation errors, add inline errors
if ($errors = $form_state->getErrors()) {
// Add error to fields using Symfony Accessor
$accessor = PropertyAccess::createPropertyAccessor();
foreach ($errors as $field => $error) {
if ($accessor->getValue($form, $field)) {
$accessor->setValue($form, $field.'[#prefix]', '<div class="form-group error">');
$accessor->setValue($form, $field.'[#suffix]', '<div class="input-error-desc">' .$error. '</div></div>');
}
}
}
}
...
<?php
/**
* @file - PARTIAL
* Contains \Drupal\my_contact\Form\ContactForm.
*/
...
/**
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
// Assert the firstname is valid
if (!$form_state->getValue('firstname') || empty($form_state->getValue('firstname'))) {
$form_state->setErrorByName('firstname', $this->t('Votre prénom est obligatoire.'));
}
// Assert the lastname is valid
if (!$form_state->getValue('lastname') || empty($form_state->getValue('lastname'))) {
$form_state->setErrorByName('lastname', $this->t('Votre nom est obligatoire.'));
}
// Assert the email is valid
if (!$form_state->getValue('email') || !filter_var($form_state->getValue('email'), FILTER_VALIDATE_EMAIL)) {
$form_state->setErrorByName('email', $this->t('Votre adresse e-mail semble invalide.'));
}
// Assert the subject is valid
if (!$form_state->getValue('subject') || empty($form_state->getValue('subject'))) {
$form_state->setErrorByName('subject', $this->t('Le sujet de votre demande est important.'));
}
// Assert the message is valid
if (!$form_state->getValue('message') || empty($form_state->getValue('message'))) {
$form_state->setErrorByName('message', $this->t('Le message de votre demande est important.'));
}
}
...
<?php
namespace Drupal\my_contact\Form;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Drupal\Core\Form\FormStateInterface;
/**
* Provides helper methods for inlining error form.
*/
trait InlineErrorFormTrait {
/**
* Apply all errors as inline field error.
*
* @param array $form
* The current form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
*/
public static function applyErrorsInline(array &$form, FormStateInterface $form_state) {
// If validation errors, add inline errors.
if ($errors = $form_state->getErrors()) {
// Add error to fields using Symfony Accessor.
$accessor = PropertyAccess::createPropertyAccessor();
foreach ($errors as $field_accessor => $error) {
try {
$accessor->getValue($form, $field_accessor);
if ($field = $accessor->getValue($form, $field_accessor)) {
$prefix = str_replace('form-group', 'form-group has-danger error', $field['#prefix']);
$suffix = '<div class="input-error-desc" id="' . $field['#id'] . '-error">' . $error . '</div>' . $field['#suffix'];
$accessor->setValue($form, $field_accessor . '[#prefix]', $prefix);
$accessor->setValue($form, $field_accessor . '[#suffix]', $suffix);
$accessor->setValue($form, $field_accessor . '[#attributes][aria-invalid]', 'true');
$accessor->setValue($form, $field_accessor . '[#attributes][aria-describedby]', $field['#id'] . '-error');
}
}
catch (\Exception $e) {
}
}
}
}
}
Copy link

ghost commented Mar 26, 2020

Anyway to disable form set error status messages ?

@WengerK
Copy link
Author

WengerK commented Mar 31, 2020

Hey @gauravmanerkar

Anyway to disable form set error status messages ?

I would highly encourage you to don't use this code anymore and use the module Inline Form Error shipped with Drupal since 8.4.x. Plus, using this module, you will be able to disable the summary see this issue: https://www.drupal.org/project/drupal/issues/2880011

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment