Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to dynamically reconfigure Drupal's jQuery-based autocomplete at runtime?

Drupal has a very well-architected, jQuery-based autocomplete.js. Usually, you don't have to bother with it, since it's configuration and execution is handled by the Drupal form API.

Now, I need a way to reconfigure it at runtime (with JavaScript, that is). I have a standard drop down select box with a text field next to it, and depending what option is selected in the select box, I need to call different URLs for autocompletion, and for one of the options, autocompletion should be disabled entirely. Is it possible to reconfigure the existing autocomplete instance, or will I have to somehow destroy and recreate?

like image 583
mikl Avatar asked Jun 19 '09 16:06

mikl


3 Answers

Have a look at misc/autocomplete.js.

/**
 * Attaches the autocomplete behavior to all required fields
 */
Drupal.behaviors.autocomplete = function (context) {
  var acdb = [];
  $('input.autocomplete:not(.autocomplete-processed)', context).each(function () {
    var uri = this.value;
    if (!acdb[uri]) {
      acdb[uri] = new Drupal.ACDB(uri);
    }
    var input = $('#' + this.id.substr(0, this.id.length - 13))
      .attr('autocomplete', 'OFF')[0];
    $(input.form).submit(Drupal.autocompleteSubmit);
    new Drupal.jsAC(input, acdb[uri]);
    $(this).addClass('autocomplete-processed');
  });
};

The input's value attribute is used to create ACDB, which is a cache of values for that autocomplete path (uri). That is used in the Drupal.jsAC function to bind the element's keydown, keyup and blur events with triggers the autocomplete ajax operation (which caches its values in the ACDB object for that element), opens popups, etc.

/**
 * An AutoComplete object
 */
Drupal.jsAC = function (input, db) {
  var ac = this;
  this.input = input;
  this.db = db;

  $(this.input)
    .keydown(function (event) { return ac.onkeydown(this, event); })
    .keyup(function (event) { ac.onkeyup(this, event); })
    .blur(function () { ac.hidePopup(); ac.db.cancel(); });

};

What you'll need to do is change the input's value and also reattach the behavior. You'll reattach the behavior by removing the '.autocomplete-processed' class on the autocomplete text field input element and then call Drupal.attachBehaviors(thatInputElement).

This may not work. Things can go very badly if you attach the same behavior to the same element over and over again. It may be more sensible to create different autocomplete fields and simply hide and show them based on the value of the select. This would still require calling Drupal.attachBehaviors when you hide and display the widget, but the same behavior would remain attached if the switch happened more than once, and you wouldn't risk attaching the same behavior to the element multiple times.

like image 63
bangpound Avatar answered Oct 23 '22 23:10

bangpound


Well, for reference, I've thrown together a hack that works, but if anyone can think of a better solution, I'd be happy to hear it.

Drupal.behaviors.dingCampaignRules = function () {
  $('#campaign-rules')
    .find('.campaign-rule-wrap')
      .each(function (i) {
          var type = $(this).find('select').val();

          $(this).find('.form-text')
            // Remove the current autocomplete bindings.
            .unbind()
            // And remove the autocomplete class
            .removeClass('form-autocomplete')
          .end()
          .find('select:not(.dingcampaignrules-processed)')
            .addClass('dingcampaignrules-processed')
            .change(Drupal.behaviors.dingCampaignRules)
          .end();

          if (type == 'page' || type == 'library' || type == 'taxonomy') {
            $(this).find('input.autocomplete')
              .removeClass('autocomplete-processed')
              .val(Drupal.settings.dingCampaignRules.autocompleteUrl + type)
            .end()
            .find('.form-text')
              .addClass('form-autocomplete');
            Drupal.behaviors.autocomplete(this);
          }
      });
};

This code comes from the ding_campaign module. Feel free to check out the code if you need to do something similar. It's all GPL2.

like image 29
mikl Avatar answered Oct 23 '22 23:10

mikl


it should be as simple as dinamically change the "value" of the "hidden" autocomplete input element that comes aside autocomplete form fields. ie.

$('select#myelement').bind('change', function(e) { 
  if (/* something */) {
    $('input#myelement-autocomplete').attr('value', '/mycustom/path');
  }
}); 
like image 1
gpilotino Avatar answered Oct 23 '22 23:10

gpilotino