Tập tành Creating custom ajax command In Drupal 9

23rd Jun 2022
Table of contents

I am writing this post to help drupal 8 developers for creating a custom Ajax Command to insert value in CKEditor in a node edit form, Like Body field of a node. Here are the steps:

Step 1:  create a custom :

for best practice create a folder named as "custom" in modules folder. Then create a folder named as your module in my case its my_custom. create following files structure.

I. my_custom.info.yml  with following code

name: my_custom
type: module
description: Custom module to handle Custom APIs and custom requirements
package: custom
core: 8.x

II. my_custom.module

with following code:

/**
* @file
* contains my_custom.module file
*/

use Drupal\Core\Form\FormStateInterface;
use Drupal\node\Entity\Node;
use Drupal\Core\Render\Element\StatusMessages;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\my_custom\Ajax\InsertInCkeditor; // this my custom class

/**
* Implementing hook_form_alter()
* @param array $form, $form_state , $form_id
*/
function my_custom_form_alter(array &$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {  
  if ($form_id == 'node_job_listing_form') {
    // attaching custom js library to node edit form.
    $form['#attached']['library'][] = 'my_custom/my-custom';
    //adding a prefix wrapper of ajax callback.
    $form['#prefix'] = '<div id="nodeJobListingForm">';
    $form['#suffix'] = '</div>';  
    //adding a prefix to body field for creating  proper selector.
    $form['body']['widget']['#prefix'] =  '<div id="job_body">';
    $form['body']['widget']['#suffix'] =  '</div>'; //job_requirements

    //select list on which change I am going to fire js event
    $jobs_list = ['india', 'US', 'Uk', 'France'];
    $form['field_job_title']['widget']['#options'] = array('_none' => '- Select -') + $jobs_list;
    $form['field_job_title']['widget']['#ajax'] = array(
      'callback' => 'updateFormValuesCallback',
      'event' => 'change',
      'wrapper' => 'nodeJobListingForm',
      'progress' => array(
        'type' => 'throbber',
        'message' => NULL,
      ),
    );

  }
}

/**
* Custom Ajax callback form change on node edit/submit form
*
**/
function updateFormValuesCallback(array $form, \Drupal\Core\Form\FormStateInterface $form_state) {
  $response = new AjaxResponse();
  $job_description = '<p>Hi this is test message.</p>';
  $response->addCommand(new InsertInCkeditor('#job_body textarea', 'val', [$job_description]));
  return $response;
}

III. my_custom.libraries.yml

In this file I will define my custom js library add following code to this file

my-custom:
  js:
    js/my-custom.js: {}
  dependencies:
    - core/drupal.ajax
    - core/jquery
    - core/ jquery.once
    - core/ drupal
    - ckeditor/drupal.ckeditor

I have added CKeditor Library as dependency for my js library, so that I will be included on page before my Library and I can use CKEditor Library to insert data.

Now Need to define Drupal\my_custom\Ajax\InsertInCkeditor class by creating new file in

my_module/src/Ajax folder

file name: will be same as class name InsertInCkeditor.php with following code:

<?php
/**
* InsertInCkeditor.php contains InsertInCkeditor class
* Defines custom ajax command for set value in CKEditor by ajax
**/
namespace Drupal\my_custom\Ajax;
use Drupal\Core\Ajax\CommandInterface;
use Drupal\Core\Asset\AttachedAssets;

/**
 * Class ExtendCommand.
 */
class InsertInCkeditor implements CommandInterface {
   /**
   * A CSS selector string.
   *
   * If the command is a response to a request from an #ajax form element then
   * this value can be NULL.
   *
   * @var string
   */
  protected $selector;

  /**
   * A jQuery method to invoke.
   *
   * @var string
   */
  protected $method;

  /**
   * An optional list of arguments to pass to the method.
   *
   * @var array
   */
  protected $arguments;

  /**
   * Constructs an InvokeCommand object.
   *
   * @param string $selector
   *   A jQuery selector.
   * @param string $method
   *   The name of a jQuery method to invoke.
   * @param array $arguments
   *   An optional array of arguments to pass to the method.
   */
  public function __construct($selector, $method, array $arguments = []) {
    $this->selector = $selector;
    $this->method = $method;
    $this->arguments = $arguments;
  }

  public function render() {
      return [
        'command' => 'InsertInCkeditor',
        'selector' => $this->selector,
        'args' => $this->arguments,
      ];  
    }
}

?>

3. Calling CKeditor Instance to put data in CKeditor my-custom.js file.  Here I am defining my custom ajax command InsertInCkeditor. Because CKEDITOR instance is not found ready at the time of firing this ajax so I have set value of command in a localStorage. So I can use them on ajaxStop() event. Here is the code.

(function ($, Drupal, CKEDITOR) {
/**
 * Add new custom command.
 */
 
 //var CKEDITOR = CKEDITOR;
  Drupal.AjaxCommands.prototype.InsertInCkeditor = function (ajax, response, status ) {     
    var textareaInstance = jQuery(response.selector).attr('id');
    var textData = response.args;  
    localStorage.setItem(textareaInstance, textData);
    //localStorage.setItem("textData", textData);        
  }

})(jQuery, Drupal, CKEDITOR);

jQuery( document ).ajaxStop(function() {
  var instance1 = 'edit-body-0-value';
  var instance2 = 'edit-field-job-requirements-0-value';
  var instance1Data = localStorage.getItem(instance1);
  var instance2Data = localStorage.getItem(instance2);
   
  /*console.log('instance1: '+ instance1);
  console.log('instance2: '+ instance2);
  console.log('instance1Data: '+ instance1Data);
  console.log('instance2Data: '+ instance2Data);*/
  if(instance1 !== undefined && instance1 != '' ) {
      CKEDITOR.instances[instance1].setData(instance1Data, {
      callback: function() {
          this.checkDirty(); // true
          localStorage.setItem(instance1, "");
        }
    } );
  }
  if(instance2 !== undefined && instance2 != "" ) {
    CKEDITOR.instances[instance2].setData(instance2Data, {
      callback: function() {
          this.checkDirty(); // true
          localStorage.setItem(instance2, "");
        }
    } );
  }
  /*for(var instanceName in CKEDITOR.instances) {  
    if(instanceName == instance) {    
      CKEDITOR.instances[instance].setData(descData, {
          callback: function() {
          this.checkDirty(); // true
          localStorage.setItem("instance", "");
          localStorage.setItem("textData", "");
        }
      } );   
    }
  }
   */
     
});

Here is the code which will let enable you to insert value in CkEditor using ajax commands programatically. if you face any bug please let know in comment I will try my best to help you.

Thanks for the tutorial, without I wouldn't manage to make it work. I changed javascript a bit to make it more universal, maybe someone will find it helpful.

(function ($, Drupal, CKEDITOR) {
  /**
   * Add new custom command.
   */
  Drupal.AjaxCommands.prototype.InsertInCkeditor = function (ajax, response, status ) {
    var selector = jQuery(response.selector).attr('id');
    var data = response.args;
    localStorage.setItem(selector, data);
  }
})(jQuery, Drupal, CKEDITOR);

jQuery( document ).ajaxStop(function() {
  for(var selector in CKEDITOR.instances) {               // Array of Ckeditor selectors on page
    if(localStorage.hasOwnProperty(selector)) {           // Do you have any data for given selector?
      var selectorData = localStorage.getItem(selector);  // Load data
      localStorage.removeItem(selector);                  // Remove data from localStorage
      CKEDITOR.instances[selector].setData(selectorData, { callback: this.checkDirty });
    }
  }
});

Add new comment

This question is for testing whether or not you are a human visitor and to prevent automated spam submissions.

Image CAPTCHA
Enter the characters shown in the image.
Câu nói tâm đắc: “Điều tuyệt với nhất trong cuộc sống là làm được những việc mà người khác tin là không thể!”

Related Articles

This configuration file is used to add links on a page that allows the user to complete an action.

By using this code in my site the class is set the first time, but after a ajax call it's not, what is wrong?

You should maybe preprocess you the URL in place. Via template_preprocess_field get the node ID, build the path alias, send it to Twig.