3

I want to create a custom form element for email that have a textfield and button and when click button send an email but ajax not working inside form elemen class. I test it in form api and it's ok but when move in form element on button click nothing happend this is my code

<?php
/**
 * Created by IntelliJ IDEA.
 * User: peter
 * Date: 24/12/18
 * Time: 13:59
 */

namespace Drupal\aqr_basic\Element;


use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element\FormElement;

/**
 * Provides a form element to display a email.
 *
 *
 * @FormElement("aqr_email")
 */
class EmailElement extends FormElement {


  public function getInfo() {
    $class = get_class($this);
    return [
      '#pre_render' => [
        [$class, 'preRenderSelect'],
      ],
    ];
  }


  public static function preRenderSelect($element) {
    dump($element);
    $email = [
      '#type' => 'email',
      '#title' => $element['#emailTitle'],
    ];
    $email = array_merge($email,$element['email']);
    $btnSendCode = [
      '#type' => 'ajax_button',
      '#value' => $element['#btnSendCodeTitle'],
      '#ajax' => [                  //////this ajax not work
        'callback' => '::email',
        'event' => 'click',
      ],
    ];

    $btnSendCode = array_merge($btnSendCode,$element['btnSendCode']);
    $verifyGroup = [
      '#type' => 'fieldgroup',
      '#attributes' => ['class' => ['hidden']],

    ];
    $verifyGroup['emailCode'] = [
      '#type' => 'textfield',
      '#title' => $element['#emailCodeTitle'],
    ];
    $verifyGroup['emailCode'] = array_merge($verifyGroup['emailCode'],$element['verifyGroup']['emailCode']);
    $verifyGroup['btnVerifyCode'] = [
      '#type' => 'ajax_button',
      '#value' => $element['#btnVerifyCodeTitle'],
      '#suffix' => '<div class="verification-desc email_verify">' . $element['#description'] . '</div>',
    ];
    $verifyGroup['btnVerifyCode'] = array_merge($verifyGroup['btnVerifyCode'],$element['verifyGroup']['btnVerifyCode']);

    $element = [
//      '#attached'=>[
//        'library' => ['aqr_basic/state'],
//        'drupalSettings' => [
//          'id' => $element['#id'],
//          'cities' => json_encode($cities),
//        ],
//      ],
      'email' => $email,
      'btnSendCode' => $btnSendCode,
      'verifyGroup' => $verifyGroup,
      '#attributes' => ['id' => ['email-wrapper-id'], 'class' => ['email_verify']],
    ];
    dump($element);
    return $element;
  }


  /**
   * {@inheritdoc}
   */
  public static function valueCallback(&$element, $input, FormStateInterface $form_state) {
    return $input;
  }

  public static function email(array &$form, FormStateInterface $form_state) {

    dump($form);

    return $form;
  }
}

3 Answers 3

1

I believe you may need this syntax as your callback, I think because everything went to being 'static' when you moved your demo code from a form to a Element.

 $btnSendCode = [
  '#type' => 'ajax_button',
  '#value' => $element['#btnSendCodeTitle'],
  '#ajax' => [                  
    'callback' => ['\Drupal\aqr_basic\Element\EmailElement', 'email'],
    'event' => 'click',
  ],
];
0

You need to add a wrapper and div ID to where you want the ajax to load

  '#ajax' => [               
    'callback' => '::email',
    'wrapper' => 'load-me-here', // we tell ajax to target this ID
    'event' => 'click',
  ],
  // attach the ID you targeted above to some element.
  '#attributes' => ['id' => 'load-me-here'],

  // you could create a new element if you want.
  // there is so many variations you can do to add the ID.

  //  $element['load'] = [
  //   '#type' => 'fieldset',
  //   '#title' => t('Load the stuff here'), 
  //   '#attributes' => ['id' => 'load-me-here'],
  //  ];

  // OR

  //  $element['load'] = [
  //   '#type' => 'markup',
  //   '#title' => t('Load the stuff here'), 
  //   '#markup' => '<div id="load-me-here"></div>', 
  //  ];

  // OR

  //  $element['load'] = [
  //   '#type' => 'fieldset',
  //   '#title' => t('Load the stuff here'), 
  //   '#prefix' => '<div id="load-me-here">',
  //   '#suffix' => '</div>',
  //  ];

Also, remove the dump($form); from your ajax email callback function, this prevents it from working.

You'll have to install Devel and use dpm($form); if you want to see the structure of the array. Also, note that Devel's kint($form); wont work on ajax callback either.

5
  • tnx I test your code but not help me and wrapper only for when you want replace html. see api.drupal.org/api/drupal/core!core.api.php/group/ajax/8.2.x
    – sadeghpro
    Commented Jan 7, 2019 at 4:58
  • @sadeghpro how else do you expect to see your dump dpm($form); and your return $form if there is no new html?
    – No Sssweat
    Commented Jan 7, 2019 at 5:19
  • my problem is there is no ajax call to have response. nothing happened when click a button. if ajax send request then I must change dump($form) and set up it.
    – sadeghpro
    Commented Jan 7, 2019 at 5:34
  • @sadeghpro like I mentioned dump($form) causes issues. Try with just return $form and get that working first.
    – No Sssweat
    Commented Jan 7, 2019 at 5:48
  • ok I remove dump($form) and only return form but nothing change. I tell you I use firefox inspect network tab there is no ajax request when click button.I think render api don't add ajax to element when I use element in another custom element
    – sadeghpro
    Commented Jan 7, 2019 at 6:06
0

Might be too late to help the OP but I ran into a similar issue with a custom RenderElement.

In my case i'm running Drupal 10.1.x. The issue was with the AJAX event attaching logic in the base RenderElement class (See: RenderElement::preRenderAjaxForm). It doesn't recognise the new element type so doesn't assign a default $element['#ajax']['event'] property.

You can either update the RenderElement to make sure a default event is assigned:

/**
 * @FormElement("my_element")
 */
class MyElement extends FormElement {

  //... Some other methods and stuff.

  /**
   * {@inheritdoc}
   */
  public static function preRenderAjaxForm($element) {
    // Do this before calling the parent because the Drupal Ajax libraries
    // are attached based on the existence of $element['#ajax']['event']
    // which won't be set in time if we do it after the parent call
    if (isset($element['#ajax']) && !isset($element['#ajax']['event'])) {
      if ($element['#type'] === 'my_element') {
        $element['#ajax']['event'] = 'change';
      }
    }

    return parent::preRenderAjaxForm($element);
  }

}

If you don't have access to modify the RenderElement (I.e. it's contributed), a work around is to make sure you define an event whenever you attach AJAX to an element of that type:

$form['some_element'] = [
  '#type' => 'my_element',
  '#title' => t('My Cool Element'),
  '#ajax' => [
    'callback' => 'someCallback',
    'wrapper' => 'some-wrapper-id',
    // Make sure you define this. Don't rely on the default
    'event' => 'change' 
  ],
];

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.