4

I have made a form for my module with a button using AJAX callback to fill in values in multiple select form another multiple select but when pressing the button the AJAX not triggered at all and when checking the logs an exception shows with the message:

Symfony\Component\HttpKernel\Exception\HttpException: The specified #ajax callback is empty or not callable. in Drupal\Core\Form\FormAjaxResponseBuilder->buildResponse() (line 67

Here is my code:

public function getFormId() {
    return 'ads_admin_form';
}

public function buildForm(array $form, FormStateInterface $form_state) {

    $form['#attached']['library'][] = 'Place_Ads/general';
    $form['cats-wrapper'] = [
        '#type' => 'container',
        '#attributes' => ['id' => 'cats-wrapper', 'class' => ['shownselect']],
    ];
    $form['bind-wrapper'] = [
        '#type' => 'container',
        '#attributes' => ['id' => 'bind-wrapper', 'class' => ['shownselect']],
    ];
    $form['cats-wrapper']['cats'] = array(
        '#type' => 'select',
        '#title' => t('Category'),
        '#options' => Place_AdsController::getClassifiedContentTypes(),
        '#ajax' => array(
            'callback' => '::catSelectedCallback',
            'wrapper' => 'bind-wrapper',
            'effect' => 'fade',
            'event' => 'change',
            'progress' => array(
                'type' => 'throbber',
                'message' => NULL,
            ),
        ),
    );
    $form['bind-wrapper']['terms-wrapper'] = [
        '#type' => 'container',
        '#attributes' => ['id' => 'terms-wrapper', 'class' => ['shownselect']],
    ];
    $form['bind-wrapper']['fields-wrapper'] = [
        '#type' => 'container',
        '#attributes' => ['id' => 'fields-wrapper', 'class' => ['shownselect']],
    ];
    $form['bind-wrapper']['terms-wrapper']['terms'] = array(
        '#type' => 'select',
        '#title' => t('Available Terms'),
        '#multiple' => TRUE,
        '#options' => NULL,
        '#size' => 7,
    );
    $form['bind-wrapper']['terms-wrapper']['addTermsBtn'] = array(
        '#type' => 'button',
        '#value' => t('>>'),
        '#ajax' => array(
            'callback' => '::termsSelectedCallback',
            'wrapper' => 'selectedterms',
            'effect' => 'fade',
            'event' => 'click',
            'prevent' => 'submit',
            'progress' => array(
                'type' => 'throbber',
                'message' => NULL,
            ),
        ),
        '#attributes' => ['class' => ['bindBtn']],
    );
    $form['bind-wrapper']['terms-wrapper']['selectedterms'] = array(
        '#type' => 'select',
        '#title' => t('Selected Terms'),
        '#multiple' => TRUE,
        '#options' => NULL,
        '#size' => 7,
    );
    $form['bind-wrapper']['fields-wrapper']['fields'] = array(
        '#type' => 'select',
        '#multiple' => TRUE,
        '#title' => t('Available Fields'),
        '#options' => NULL,
        '#size' => 7,
    );
    $form['bind-wrapper']['fields-wrapper']['addFieldsBtn'] = array(
        '#type' => 'button',
        '#value' => t('>>'),
        //'#executes_submit_callback' => FALSE,
        //'#limit_validation_errors' => FALSE,
        '#attributes' => ['class' => ['bindBtn'], 'onclick' => 'return (false);'],
    );
    $form['bind-wrapper']['fields-wrapper']['selectedfields'] = array(
        '#type' => 'select',
        '#multiple' => TRUE,
        '#title' => t('Selected Fields'),
        '#options' => NULL,
        '#size' => 7,
    );

    $form['bindFields'] = array(
        '#type' => 'submit',
        '#value' => t('Save'),
        '#attributes' => ['id' => 'bindFieldsBtn'],
        '#submit' => ['::submitForm'],
    );
    return $form;
}

public function catSelectedCallback(array &$form, FormStateInterface $form_state) {
    $val = $form_state->getValue('cats');
    $terms = Place_AdsController::getsubCategory($val, 10);
    $currFields = Place_AdsController::contentTypeFields($val);
    unset($terms[current(array_keys($terms))]);
    $form['bind-wrapper']['terms-wrapper']['terms']['#options'] = $terms;
    //$form['bind-wrapper']['terms-wrapper']['#attributes']['class'] = ['shownselect'];
    $form['bind-wrapper']['fields-wrapper']['fields']['#options'] = $currFields;
    //$form['bind-wrapper']['fields-wrapper']['#attributes']['class'] = ['shownselect'];

    return $form['bind-wrapper'];
}

public function termsSelectedCallback(array &$form, FormStateInterface &$form_state) {
    $selected_index = $form_state['values']['terms']['und'];
    $val = $form_state->getValue('terms');
    $form['bind-wrapper']['terms-wrapper']['selectedterms']['#options'] = $val;
    return $form['bind-wrapper']['terms-wrapper']['selectedterms'];
}

public function submitForm(array &$form, FormStateInterface $form_state) {
    drupal_set_message('Nothing Submitted. Just an Example.');
    $new_node = array();
    foreach ($form_state->getValues() as $key => $value) {
        $new_node[$key] = $value;
    }
}

2 Answers 2

5

Not totally sure but I think that the problem is that both buttons (addTermsBtn and addFieldsBtn) have the same #value. Try to use different #value for the buttons or use #name attribute for differentiate them.

2
  • Wow.. its really working now with changing the value. even if the values are kept equal and given #name attribute for each one solved the problem thanks to you @marco Commented Sep 4, 2016 at 20:03
  • Had the same problem with multiple textfields with same value but same (read: not specified) name. Giving index names to textfields helped.
    – NicklasF
    Commented Jul 29, 2022 at 21:43
2

Change your callbacks to be like this:

'callback' => [$this, 'catSelectedCallback'],
4
  • not working, besides it's actually the button ajax that fire the exeption not the select ajax Commented Sep 4, 2016 at 10:14
  • Why should he? The ::methodName notation is specifically supported by Drupal just to be a shorthand to this longer form.
    – Gábor
    Commented May 26, 2018 at 11:00
  • For some reason it worked for me, and ::submitForm notation didn't. So there has to be a difference. Commented Aug 19, 2019 at 13:23
  • Yes, and it's really hidden: [$this, 'callback'] vs. '::callback' The difference is, that the second is not an array (no brackets)... Prone to bugs! Commented Oct 19, 2023 at 9:00

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.