1

I have a small email signup form made from extending FormBase that takes an email address and submits it to a mail service. I need to change the submit to AJAX so I can flash a success or error message.

I want to show this form more than once on a page. Once in a CTA near the top of the page, and there is a slightly different styled version in the footer.

When I added #ajax to the submit button, only one instance of the form seems to be affected. Here is the whole form:

$info = $form_state->getBuildInfo();

$form['#prefix'] = '<section class="hl__single-input-form">';
$form['#suffix'] = '</section>';
$form['#attributes']['class'][] = 'hl__single-input-form__form';

$form['title'] = [
  '#type' => 'label',
  '#for' => 'edit-email-address',
  '#title' => 'Stay in the know',
  '#attributes' => [
    'class' => [
      'hl__label',
      'hl__label--inline',
    ],
  ],
];

if (!empty($info['args'][0]) && $info['args'][0] == "show_tagline") {
  $form['tagline'] = [
    '#markup' => '<div class="hl__single-input-form__helper-text">Sign up for email updates from MySite</div>',
  ];
}

$form['email_address'] = [
  '#type' => 'email',
  '#size' => NULL,
  '#required' => TRUE,
  '#attributes' => [
    'placeholder' => $this->t('Email'),
    'class' => [
      'hl__input',
      'hl__input--inline',
      'js-is-required',
    ],
    'data-twig-suggestion' => 'newsletter_signup_email',
  ],
  '#prefix' => '<div class="hl__single-input-form__input-group">',
];

$form['submit'] = [
  '#type' => 'submit',
  '#value' => $this->t('Sign up'),
  '#attributes' => [
    'class' => [
      'hl__button',
      'hl__button--small',
      'hl__button--inline',
    ],
    'data-twig-suggestion' => 'newsletter_signup_submit',
  ],
  '#suffix' => '</div>',
  '#ajax' => [
    'wrapper' => 'hl__single-input-form',
  ]
];

The block in the footer that pulls the form just has this build method:

  /**
   * {@inheritdoc}
   */
  public function build() {
    return ['form' => $this->formBuilder->getForm('\Drupal\harlib_newsletter_signup\Form\NewsletterSignup')];
  }

The preprocess for the Paragraph that outputs the form when being themed:

$variables['signup_form'] = \Drupal::service('form_builder')->getForm('\Drupal\harlib_newsletter_signup\Form\NewsletterSignup', "show_tagline");

How can I support having this form on the page more than once, both with the same AJAX action on submit? Also, how can I avoid overwriting the wrong wrapper with an AjaxCommand?

edit: validateForm method:

/**
   * {@inheritdoc}
   */
  public function validateForm(array &$form, FormStateInterface $form_state) {
    $email_value = $form_state->getValue('email_address');
if (empty($email_value) || !\Drupal::service('email.validator')->isValid($email_value)) {
  $form_state->setError($form['email_address'], $this->t('Please enter a valid email address.'));
}

if (mailchimp_is_subscribed('XXXXXXX', $email_value, TRUE)) {
  $form_state->setError($form['email_address'], $this->t('You are already subscribed to our newsletter.'));
}
  }

1 Answer 1

1

Define a class property for a sub id and append it to the form id:

class MyForm extends FormBase {

  public $sub_id = '0';

  public function getFormId() {
    return 'my_form_' . $this->sub_id;
  }

  ...
}

You can append this sub id to the wrapper as well, if you want to have different wrappers for each form:

  public function buildForm(array $form, FormStateInterface $form_state) {
    $wrapper = 'ajax-wrapper-' . $this->sub_id;
    // use $wrapper for #ajax and div container in buildForm()
    ...
  }

Then set a different sub id for each instance of the form.

For example in a block the first instance:

$form = \Drupal::classResolver()->getInstanceFromDefinition('Drupal\mymodule\Form\MyForm');
$form->sub_id = '1';
$build['form'] = \Drupal::formBuilder()->getForm($form);

and the second in preprocess:

$form = \Drupal::classResolver()->getInstanceFromDefinition('Drupal\mymodule\Form\MyForm');
$form->sub_id = '2';
$variables['form'] = \Drupal::formBuilder()->getForm($form);
5
  • This was the road I started down, but didn't change getFormId. Let me try this.
    – Kevin
    Commented Jan 17, 2018 at 19:52
  • This might have me on the right path, but how can I return an error from the validateForm method? Nothing is coming through. Updated OP.
    – Kevin
    Commented Jan 17, 2018 at 21:23
  • This shouldn't be specific to this topic about multiple forms, I've managed to return validation errors to the correct form instance.
    – 4uk4
    Commented Jan 17, 2018 at 21:57
  • I’m not getting them at all, weird. I’ll open a new question.
    – Kevin
    Commented Jan 17, 2018 at 22:01
  • Actually, I think I have been running in circles due to this. Now everything seems to work fine. drupal.org/project/drupal/issues/1342066#comment-12433457
    – Kevin
    Commented Jan 18, 2018 at 21:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.