Jul 26, 2014

Implementing an Ajax call on form checkboxes

On the Project Listing page of the Optimizely module, each project has a checkbox that the user can use to enable or disable the project. The handling of that user action is implemented by use of an Ajax call back to the server to validate the action and to update the database if the action is accepted. The server then returns the appropriate response.

The D7 code from hook_menu() is,

  $items['ajax/optimizely'] = array(
    'title' => 'Optimizely Administer AJAX',
    'page callback' => 'optimizely_ajax_enable',
    'access arguments' => array('administer optimizely'),
    'file' => 'optimizely.admin.inc',
    'file path' => drupal_get_path('module', 'optimizely'),
    'type' => MENU_CALLBACK,

To do the conversion to Drupal 8, first, there was the need to define a route in the optimizely.routing.yml file to replace the above entry in hook_menu(). This route is similar to the ones that were defined for menu pages but there are critical differences.

The most important difference is that instead of the _form or the _content property, the _controller property is used. Whatever is returned by the indicated method is then returned as is without further processing. In this case it is a Json response to the Ajax call.

The other difference is that the _format property specifies that the request must be in Json.

  path: /ajax/optimizely
    _controller: \Drupal\optimizely\AjaxEnable::enableDisable
    _title: Optimizely Administer AJAX
    _permission: administer optimizely
    _format: json

Second, implementing the method to be called. The conversion of the code to Drupal 8 required replacing calls to drupal_json_output() with instantiating instances of class JsonResponse. The arguments remain the same.

namespace Drupal\optimizely;

use Symfony\Component\HttpFoundation\JsonResponse;

class AjaxEnable {

  public static function enableDisable() {

    // Code to validate the user's action, 
    // update the database, etc.

    if (. . . ) {

     $options = array('status' => 'updated', 
                        'oid' => $target_oid,
                        'target_enable' => $target_enable,
                        'message' => $message);

      return new JsonResponse($options);
    else {
      $options = array('status' => 'rejected', 

                        'oid' => $target_oid,
                        'issue_path' => $target_path,
                        'message' => $message);

      return new JsonResponse($options);

Finally, there was an edit that had to be made to the jQuery code on the client side. Drupal.settings is now drupalSettings, and the following line, 

    'url': Drupal.settings.basePath + 'ajax/optimizely',

was replaced with,

    'url': drupalSettings.path.basePath + 'ajax/optimizely',

Update: see  http://optimizely-to-drupal-8.blogspot.com/2015/01/beta-4-drupalmatchpath-and.html


Routing system in Drupal 8

Structure of routes

drupal_json_output is removed in favor of Symfony\Component\HttpFoundation\JsonResponse

Ajax framework
This is a good explanation of an Ajax framework that is provided in D8. 

replace all occurence of Drupal.settings with drupalSettings


  1. I assume the database functionality that the AJAX triggers still needs to be converted? Would it be worthwhile testing the code in github in it's current state?

    1. The Ajax / checkbox functionality is complete. Paths are validated, other validation is done, and the enabled / disabled flag in the database is updated.

      I believe it's worth testing the current set of code in github. That would be helpful to me.