Drupal Problems and Solutions

DrupalABC aims to provide solution to programming problems faced by Drupal developers.Start learning by reading some of the latest problems below-

How to fetch Drupal node data in Angular JS

Angular JS can be used for creating a headless Drupal website. To fetch a node data in angular you have to first expose nodes data by enabling REST related modules provided by Drupal 8 core. Once that has been done, you simply have to send an HTTP get a request from Angular to got JSON data of Drupal node.

Here is the angular JS code to achieve that 

 

$http.get("http://exampledrupalwebsite.com/node/1?_format=json") .then(function(response) { $scope.node_data = response.data; });

Variable Node Data can then be used to display node information in HTML.

How to enable CORS in Drupal 8

Most of the Drupal websites are getting converted to Headless applications. Most important configuration to achieve that is by enabling CORS in Drupal. If CORS is not enabled your headless app will not be able to fetch data from Drupal. To do that : 

1. Copy default.services.yml and create file services.yml.

2. Go to the last configuration in the file i.e CORS settings. Modify it in this way to allow cross-origin requests.

   # Configure Cross-Site HTTP requests (CORS).

   # Read https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS

   # for more information about the topic in general.

   # Note: By default the configuration is disabled.

  cors.config:

    enabled: true

    # Specify allowed headers, like 'x-allowed-header'.

    allowedHeaders: ['x-csrf-token', 'authorization', 'content-type', 'accept', 'origin', 'x-requested-with']

    # Specify allowed request methods, specify ['*'] to allow all possible ones.

    allowedMethods: ['POST', 'GET', 'OPTIONS', 'DELETE', 'PUT', 'PATCH']

    # Configure requests allowed from specific origins.

    allowedOrigins: ['*']

    # Sets the Access-Control-Expose-Headers header.

    exposedHeaders: true

    # Sets the Access-Control-Max-Age header.

    maxAge: false

    # Sets the Access-Control-Allow-Credentials header.

    supportsCredentials: false

 

How to provide menu route parameters in Drupal 8

In Drupal 8 by appropriate use of routing.yml and callback function/class, you can send values through URL. For eg, you a URL of type /data/{user-id}, on reaching this URL data should be loaded according to the user-id. This can be achieved in following way :

custom_module.admin_data.uqid.remove.uid:
  path: '/admin/custom_form/{uqid}/remove/{uid}'
  defaults:
    _form: '\Drupal\custom_module\Form\UserRemoveConfirmForm'
    _title: 'Remove User'
    custom_arg: '{uqid},{uid}'
  requirements:
    _permission: 'administer custom module'

Through .yml we are allowing user to send 2 values i.e uqid and uid. Now these can be fetched in Form class in following ways : 

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

//Here you can access both $uqid and $uid.

}

 

How to handle form submission using AJAX in Drupal 8

Here is the buildForm function for creating an AJAX form.

  public function buildForm(array $form, FormStateInterface $form_state) {
  
    $form = [];
    $form['email'] = [
      '#type' => 'textfield', 
      '#attributes' => ['placeholder'=>'Enter your Email ID'],
      '#description' => 'Submit your email address to recieve updates',
      '#size' => 100,
      '#maxlength' => 150,
      '#required' => TRUE,  
    ];

    $form['submit_button'] = [
      '#type' => 'submit',
      '#name' => 'submit-email',
      '#value' => '>',
      '#ajax' => [
        'callback' => '::processEmail',
        'wrapper' => 'thanks-message',
        'effect' => 'fade',
        'event' => 'click',
        'progress' => [
          'type' => 'throbber',
          'message' => $this->t('Processing ...'),
        ],
      ],
    ];

    $form['thanks'] = [
     '#type' => '#markup',
     '#markup'=> '',
     '#prefix' => '<div id="thanks-message">',
     '#suffix' => '</div>'
    ];

    $form['error'] = [
     '#type' => '#markup',
     '#markup'=> '',
     '#prefix' => '<div id="error-message">',
     '#suffix' => '</div>'
   ];

    return $form;
  }

And this the code for callback function.

  public function processEmail (array &$form, FormStateInterface $form_state) {
    $form['#cache'] = ['max-age' => 0];
    $email = $form_state->getValue('email');
    $renderer = \Drupal::service('renderer');
    $response = new AjaxResponse();

    if (!(\Drupal::service('email.validator')->isValid($email))) {
      $elem = [
        '#type' => 'markup',
        '#markup'=> 'Please provide correct Email ID',
        '#prefix' => '<div id="error-message">',
        '#suffix' => '</div>'
      ];
      $response->addCommand(new InsertCommand('#error-message', $renderer->render($elem)));
      return $response;
    }
    else {
      $element = [
        '#type' => 'markup',
        '#markup'=> 'Thank you! Please check your email for confirmation message.',
        '#prefix' => '<div id="thanks-message">',
        '#suffix' => '</div>'
      ];

      $error = [
        '#type' => 'markup',
        '#markup'=> '',
        '#prefix' => '<div id="error-message">',
        '#suffix' => '</div>'
      ];

      //$elma used as an empty variable to hide unwanted element.s

      $response->addCommand(new ReplaceCommand('#thanks-message', $renderer->render($element)));
      $response->addCommand(new ReplaceCommand('#edit-submit-button', $renderer->render($elema)));
      $response->addCommand(new ReplaceCommand('#edit-email--description', $renderer->render($elema)));
      $response->addCommand(new ReplaceCommand('#edit-email', $renderer->render($elema)));
      $response->addCommand(new RemoveCommand('#error-message'));
      return $response;
    }
  }

How to handle form submission using JS in Drupal 7

In Drupal 7 you use behaviors to handle the form submission. It can be achieved in following way.

  $form = array();
  $form['sample_value'] = array(
    '#type' => 'textfield',
    '#title' => 'Sample Value',
    '#size' => 20,
    '#maxlength' => 150,
    '#required' => TRUE,
  );

 //Make sure you set Drupal form submission to be false.

  $form['#executes_submit_callback'] = FALSE;

  $form['submit_button'] = array(
    '#type' => 'submit',
    '#value' => t('Submit'),
  );

 //Attach the JS file to form.

$form['#attached']['js'][] = drupal_get_path('module', 'custom_module') . '/js/custom.js';

Now in your JS file, you can access the values in following ways 

/**
 * @file Javascript behaviors for the Custom module.
 */

(function ($) {
  // Make sure our objects are defined.
  Drupal.CustomForm = Drupal.CustomForm || {};

  Drupal.CustomForm.compute = function(form) {
     //You can perform any kind of operation from value variable. 

     value = form.sample_value;
  }

  Drupal.behaviors.custom = {
    attach: function (context, settings) {

      $("#edit-submit-button", context).click(function(event) {
        Drupal.CustomForm.compute(this.form);
        return false;
      });
    }
  };
})(jQuery);

How to load a block programmatically and use in twig file

There might be a situation when you have to print a block at a specific location on the webpage. In that case, you can load the block and set up a variable which will be printed in twig file. This can be achieved by following way:

Add the following code in right preprocess in the .theme file.

  $block = Block::load('test_block');
  $variables['test_block'] = \Drupal::entityTypeManager()
    ->getViewBuilder('block')
    ->view($block);

Now in your twig file you print test block in following way {{ test_block }}.

How to load field collection programmatically in Drupal 8

Field collection can be used in case a field has subfields. For eg Name can have subfields such as First Name, Middle Name, and Last Name. There might be a requirement of loading field collection items programmatically. It can be done in following way:

$fieldCollection = \Drupal\field_collection\Entity\FieldCollectionItem::load($fieldCollectionId);
$firstName = $fieldCollection->get('field_first_name')->getValue();

It is better to use Paragraph module instead of using field collection, becuase it has better features and is being well maintained.