1

I am modifying elements from the multiselect module to add ajax functionality. I wanted to use hook_field_widget_WIDGET_TYPE_form_alter() but I'm stuck in 7.14 and it requires 7.8.

So, I'm traversing the $form in hook_form_alter() to find the modify the 'multiselect' elements:

/*
 * Implements hook_form_alter()
 */
function ajax_multiselect_form_alter(&$form, &$form_state, $form_id) {
  _ajax_multiselect_search_children($form, $form);
}

/*
 * Look for multiselect elements
 */
function _ajax_multiselect_search_children(&$form, &$element) {

  if (isset($element['#type']) && $element['#type'] == 'multiselect') {
    _ajax_multiselect_modify_multiselect($form, $element);
  }

  $children = element_children($element);

  if (is_array($children)) {
    foreach ($children as $child) {
      _ajax_multiselect_search_children($form, $element[$child]);
    }
  }
}

When I find a 'multiselect' element, I add a new 'select' element to the form with the #ajax property copied from the original 'multiselect'.

/*
 * Modify the multiselect for ajax processing
 */
function _ajax_multiselect_modify_multiselect(&$form, &$element) {

  $element['#attached']['js'] = array(
    array(
      'data' => drupal_get_path('module', 'ajax_multiselect') . '/js/ajax_multiselect.js',
    ),
  );


  if (isset($element['#ajax'])) {

    $ajax_element_id = drupal_html_id('ajax_multiselect_element');

    // tell jquery where to find the id of the dummy select
    $element['#attributes']['class'][] = 'multiselect-element-id-holder';
    // transmit the id of the dummy select to jquery
    $element['#attributes']['data-ajax-multiselect-element-id'] = $ajax_element_id;

    $form[$ajax_element_id] = array(

      '#id' => $ajax_element_id,
      '#type' => 'select',
      '#ajax' => $element['#ajax'],
      '#options' => array(0,1),

//      '#prefix' => '<div style="display:none">',
//      '#suffix' => '</div>',
    );

  }

}

Then, in Javascript, I trigger a 'change' event on the new (dummy) 'select' whenever the items in the 'multiselect' are altered.

  // create a new function that incorporates the old function to add/remove options in multiselect
  $.fn.oldAddOption = $.fn.addOption;
  $.fn.addOption = function(arguments) {
    this.oldAddOption(arguments);

    // find the tag that holds the id of the ajax multiselect element
    var tag_with_id = $(this).parent().parent().find('.multiselect-element-id-holder');
    // find the id of the ajax multiselect element
    var ajax_element_id = tag_with_id.data('ajax-multiselect-element-id');

    $('#'+ajax_element_id).trigger('change');
  }

This works for the first #ajax round trip, but after the first #ajax call anywhere on the page, my new (dummy) 'select' elements don't fire the correct #ajax. In my Drupal Recent Logs, I get

Undefined index: #ajax in ajax_form_callback() 

when I use the dummy select elements.

In network activity, I can see that the first #ajax response includes the correct array of commands for the replace action. But, after the first #ajax anywhere on the form, my new 'select' elements only receive a default #ajax settings response with no replace commands.

I think there is something about the way I am building in hook_form_alter that is not adding the #ajax property correctly after the first ajax call, but I can't figure out what it is.

1

The problem was relying on the IDs created by drupal_html_id() to match after ajax rebuild of the form. I fixed it by having jQuery find the dummy select elements by 'name' attribute.

  $.fn.oldAddOption = $.fn.addOption;
  $.fn.addOption = function(arguments) {
    this.oldAddOption(arguments);

    // find the tag that holds the id of the ajax multiselect element
    var tag_with_name = $(this).parent().parent().find('.multiselect-element-name-holder');
    // find the id of the ajax multiselect element
    var ajax_element_name = tag_with_name.data('ajax-multiselect-element-name');

    $('select[name="'+ajax_element_name+'"]').trigger('change');
  }

This code now sets unique element names instead of trying to set unique ids:

$ajax_element_name = drupal_html_id('ajax-multiselect-element');

// tell jquery where to find the id of the dummy select
$element['#attributes']['class'][] = 'multiselect-element-name-holder';
// transmit the id of the dummy select to jquery
$element['#attributes']['data-ajax-multiselect-element-name'] = $ajax_element_name;

$form[$ajax_element_name] = array(
  '#type' => 'select',
  '#ajax' => $element['#ajax'],
  '#options' => array(0,1),
);
| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.