Jak na našeptavač (autocomplete) s více parametry

Stalo se vám někdy, že jste potřebovali předávat funkci, která generuje „nápovědu“ pro textové políčko s automatickým doplňováním více parametrů? Mě se to přihodilo zrovna dneska … 

DISCLAIMER: Jen pro Drupal geeky!

Vytvořil jsem u příslušného elementu ve formuláři parametr #autocomplete_path, jak se praví v dokumentaci k Drupal Forms API (FAPI). Vytvořil routu v hook_menu pro autocomplete callback. Vytvořil jsem dokonce už samotnou funkci na napovídání... a teprve při psaní hlavičky funkce mi to došlo. Jak sakryš přidat další parametr kromě části vyhledávaného řetězce? Potřeboval jsem totiž předat ještě jeden parametr z formuláře, který ovlivňuje vyhledávání. A to bohužel Drupal nepodporuje.

Bez trocha Javascriptu to nepůjde, to mi už bylo jasné, bude nutné vytvořit handler, který při změně podmínek formuláře dynamicky změní URL autocomplete elementu.

Podíval jsem se přes Firebug na DOM jestli nevykoukám, kde můžu URL změnit... Nic jsem rychle neobjevil - přišel čas na strejdu Googla. Po chvíli googlení jsem se dostal na toto vlákno na Stackoverflow: How to dynamically reconfigure Drupal’s jQuery-based autocomplete at runtime?. Zde je celkem pěkně popsána inicializace autocomplete. Zaujal mě zde však celkem nenápadný komentář od Johna Fialy: „Note that this is pretty much what the location module does - have a look at location_autocomplete.js for the juicy details. – John Fiala Dec 9 at 22:43“.

Modul Location obsahuje dvě provázaná pole země (selectbox ) a provincie (textové pole s našeptávačem). Když se změní země, vymaže se obsah pole provincie a našeptávač by měl (logicky) začít nabízet provincie z aktuálně nastavené země. Stáhnul jsem tedy modul Location a jal se studovat, jak to tedy dělá... No a po 30 min bylo hotovo :-) Níže je výpis stěžejních pasáží kódu.

Výpis location.module řádek 634-671:

<?php
case 'province':
    drupal_add_js(drupal_get_path('module', 'location') .'/location_autocomplete.js');
    $country = $a5['country'] ? $a5['country'] : variable_get('location_default_country', 'us');
    return array(
        '#type' => 'textfield',
        '#title' => t('State/Province'),
        '#autocomplete_path' => 'location/autocomplete/'. $country,
        '#default_value' => $obj,
        '#size' => 64,
        '#maxlength' => 64,
        '#description' => NULL,
        // Used by province autocompletion js.
        '#attributes' => array('class' => 'location_auto_province'),
        '#required' => ($a4 == 2),
    );

case 'country':
    // … vypuštěno
    return array(
        '#type'  => 'select',
        '#title'          => t('Country'),
        '#default_value'  => $obj,
        '#options'        => $options,
        '#description'    => NULL,
        '#required'       => ($a4 == 2),
        // Used by province autocompletion js.
        '#attributes'     => array('class' => 'location_auto_country'),
    );
}
?>

Výsek kódu je z funkce která parametrizovaně generuje formulářová políčka lokace. Zde je dobré všimnout si přidání souboru s javascriptem, nastavení výchozí #autocomplete_path a označení elementů třídami 'location_auto_country' a 'location_auto_province'.

Následuje mírně zjednodušený výpis location_autocomplete.js:

Drupal.behaviors.location = function(context) {
  $('select.location_auto_country:not(.location-processed)', context).change(function(e) {
    var obj = this;
    // mírně zjednodušeno
    // políčko s provincií
    var input =  $('.location_auto_province');    
    if (input && input.length) {
      
      //Unbind events on province field and empty its value
      input.unbind().val('');
      input.each(function(i) {

        //Get the (hidden) *-autocomplete input element
        var input_autocomplete = $('#' + this.id + '-autocomplete');
        
        // Update autocomplete url
        input_autocomplete.val(input_autocomplete.val().substr(0, input_autocomplete.val().lastIndexOf('/') + 1) + $(obj).val());
        
         // Mark as not processed.
        input_autocomplete.removeClass('autocomplete-processed');

      });
      
      // Reprocess.
      Drupal.behaviors.autocomplete(document);
    }
  }).addClass('location-processed');
};

Kód pracuje ve jmenném prostoru Drupal.behaviors, což je standardní cesta, jak v Drupalu přiřazovat elementům stránky nějaké chování. Jak je vidět skriptík se spustí při kliknutí na výběr země (select.location_auto_country). Poté se vyhledá políčko s provincií (location_auto_province) a uloží se do proměnné input.

Provincie je vymazána (val('')), jsou odstřiženy všechny akce (unbind). Poté je vyhledán skrytý prvek s našeptávačem, který je uložen do proměnné input_autocomplete. Zde konečně dochází ke změně URL: na konec URL se přidává parametr získaný jako hodnota z formulářového prvku s výběrem země.

Nyní zbývá už jen našeptavač zinicializovat. Odstraní se třída autocomplete-processed, která by opětovné inicializaci zabránila a následně se připojí našeptávač k prvku znovu: Drupal.behaviors.autocomplete(document), v tomto případě se jako argument předává celý HTML dokument.

A to je vše ;-)

POZNÁMKA: „Správnější“ cestou jak přiřazovat opětovně Drupal.behaviors je použít funkci Drupal.attachBehaviors(context). Tím dosáhneme připojení všech „behaviors“ k prvku/části dokumentu, který předáme jako argument. Spuštěním Drupal.behaviors.autocomplete(context) dosáhneme pouze připojení „autocomplete“ (a ničeho jiného).

Článek vyšel původně na mém Drupal.cz blogu http://www.drupal.cz/blog/wojtha/jak-na-naseptavac-autocomplete-s-vice-parametry