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.

ajax.enable:
  path: /ajax/optimizely
  defaults:
    _controller: \Drupal\optimizely\AjaxEnable::enableDisable
    _title: Optimizely Administer AJAX
  requirements:
    _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

Sources:

Routing system in Drupal 8
https://www.drupal.org/node/2122071

Structure of routes
https://www.drupal.org/node/2092643

drupal_json_output is removed in favor of Symfony\Component\HttpFoundation\JsonResponse
https://www.drupal.org/node/1665684

Ajax framework
https://api.drupal.org/api/drupal/core%21core.api.php/group/ajax/8.0.x
This is a good explanation of an Ajax framework that is provided in D8. 

replace all occurence of Drupal.settings with drupalSettings
https://www.drupal.org/node/1793648

2 comments:

  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?

    ReplyDelete
    Replies
    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.

      Delete