I've added an ajax callback to my form to add some data on it :

$form['add'] = [
    '#type' => 'button',
    '#value' => t('add'),
    '#ajax' => [
        'callback' => [$this, 'ajaxFormAdd'],
        'event' => 'click',
        'wrapper' => 'contacts',
        'progress' => [
            'type' => 'throbber',
            'message' => $this->t('Ajout d\'un contact...'),

This is how I render the new fields (they're appended into a specific div)

public function ajaxFormContact(array &$form, FormStateInterface $form_state) {
    /** @var \Drupal\Core\Ajax\AjaxResponse $response */
    $response = new AjaxResponse();

    $index = 0;
    $collapseId = 'collapse-contact-' . $index;
    $toggle = '<button class="btn btn-primary collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#' . $collapseId . '" aria-expanded="true" aria-controls="' . $collapseId . '">Contact ' . $index . '</button>';
    $collapse = '<div class="collapse show" id="' . $collapseId . '"><div class="card card-body"></div></div>';
    $contact = $this->buildSingleContact($index);
    $response->addCommand(new AppendCommand('#contacts', $toggle . $collapse));
    $response->addCommand(new AppendCommand('#' . $collapseId . ' > .card', $contact));
    return $response;

And those are the fields added :

public function buildSingleContact(int $index) {
    $contact_single['contacts'][$index]['firstname'] = [
        '#type' => 'textfield',
        '#title' => 'firstname',
        '#attributes' => [
            'data-drupal-selector' => "edit-contacts-$index-firstname",
            'id' => "edit-contacts-$index-firstname",
            'name' => "contacts[$index][firstname]",
    $contact_single['contacts'][$index]['extraInfo'] = [
        '#type' => 'checkbox',
        '#title' => $this->t('Add info'),
        '#prefix' => '<div class="form-check form-switch">',
        '#suffix' => '</div>',
        '#attributes' => [
            'data-drupal-selector' => "edit-contacts-$index-extraInfo",
            'id' => "edit-contacts-$index-extraInfo",
            'name' => "contacts[$index][extraInfo]",
        '#ajax' => [
            'callback' => [$this, 'ajaxFormExtra'],
            'event' => 'change',
            'progress' => [
                'type' => 'throbber',
    return $contact_single;

So this is my problem:

The ajax on my button is working because the button is on the page when the page is loaded.

But the checkbox extraInfo has no event, because the contact is created after the js is loaded.

So how do I add the change event on my newly added checkboxes ? Is it possible to add like a "$(selector).on('click')" instead of "$(selector).click()" ?


1 Answer 1


Nop :) the problem on your link is that the input are not submitted, my problem is that the checkbox doesn't have the event because it's generated after the page is loaded

When adding form elements too late in an ajax callback you're not only preventing input values to be submitted, but also you're bypassing the processing steps.

For a checkbox element these are:

  '#process' => [
    [$class, 'processCheckbox'],
    [$class, 'processAjaxForm'],
    [$class, 'processGroup'],

In your case you need processAjaxForm calling preRenderAjaxForm to attach the necessary JavaScript.

So add the form elements already in buildForm():

$form['contact'][$index] = $this->buildSingleContact($index);

Check $form_state->getTriggeringElement() for the right time to add the new fields.

The ajax callack is then only a delivery method for the dynamically added parts

$response->addCommand(new AppendCommand('#' . $collapseId . ' > .card', $form['contact'][$index]));

instead of the entire $form in a non-Ajax rebuild.

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.