Autocomplete in cascata

 
Interfaccia di esempio per la selezione tramite autocompletamento in cascata

Come spesso capita si utilizzano select per andare a far effettuare delle scelte agli utenti, e come abbiamo visto precentemente è possibile usare le funzionalità ajax per fare in modo che delle select secondarie varino in funzione della prima scelta.

Cosa succede, invece, quando si hanno centinaia o migliaia di elementi tra cui scegliere? una select diventa una pessima scelta per l'utente che deve andare ad inserire i dati, ma fortunatamente Drupal ci mette a disposizione i campi autocompletanti, elementi che abbiamo già affrontato in articoli precedenti.

Vediamo ora come creare dei campi autocompletanti che hanno dipendenze tra di loro.

Per poter realizzare questa dipendenza potremmo realizzare un meccanismo javascript che prelevi l'informazione dal campo precedentemente completato e la trasferisca al server per poter avere una risposta coerente con la scelta effettuata in precedenza. Questa operazione può essere effettuata andando a interagire con il sistema di autocompletamento di Drupal, che sfruttando le interfaccie di javascript può essere sovrascritto (anche solo in parte).

Nel caso specifico è necessario andare a modificare l'oggetto Drupal.ACDB ed in particolare la sua funzione di search. Questa funzione deventa quindi come di seguito indicata.

Drupal.ACDB.prototype.search = function (searchString) {
  var db = this;
  // Language ID
  var langId = this.owner.input.lang;
  // Previous text
  var prevText = '';
  // Current ID
  var currentId = this.owner.input.id.replace('edit-', '');
  // Check if is first element
  if(langId > 0) {
    var selectorString = new Array();
    for( var i = langId - 1; i >= 0; i-- ) {
      selectorString[langId - i] = 'input.dependence-autocomplete#edit-' + i;
    }
    // Create list of previous item
    $(selectorString.join(', ')).each(function () {
      if(this.value != '') {
        prevText += '/' + Drupal.encodeURIComponent(this.value);
      } else {
        prevText += '/' + Drupal.encodeURIComponent('#');
      }
    });
  }
  // Clear all children element when this element change
  $('input.dependence-autocomplete').each(function () {
    if($(this).attr('lang') > langId) {
      this.value = '';
    }
  });
  this.searchString = searchString;
  // See if this key has been searched for before
  if (this.cache[searchString + '/' + currentId + prevText]) {
    return this.owner.found(this.cache[searchString + '/' + currentId + prevText]);
  }
  // Initiate delayed search
  if (this.timer) {
    clearTimeout(this.timer);
  }
  this.timer = setTimeout(function() {
    db.owner.setStatus('begin');
    // Ajax GET request for autocompletion
    $.ajax({
        type: "GET",
        url: db.uri + '/' + Drupal.encodeURIComponent(searchString) +
                      '/' + Drupal.encodeURIComponent(currentId) +
                      prevText,
        dataType: 'json',
        success: function (matches) {
          if (typeof matches['status'] == 'undefined' || matches['status'] != 0) {
            db.cache[searchString + '/' + currentId + prevText] = matches;
            // Verify if these are still the matches the user wants to see
            if (db.searchString == searchString) {
              db.owner.found(matches);
            }
            db.owner.setStatus('found');
          }
        },
        error: function (xmlhttp) {
          alert(Drupal.ahahError(xmlhttp, db.uri));
        }
      });
    }, this.delay);
};

Questa funzione, ovviamente, non deve sostituire la funzione originale, ma sovrascriverla, quindi la inseriremo in un file javascript che verrà incluso nel form che conterrà l'autocompletamento. Vediamo velocemente cosa fa questa funzione una volta modificata.

La prima marte si occupa di recuperare il valore dell'attributo lang dell'oggetto che effettua l'autocompletamento, successivamente recupera l'ID dell'elemento corrente e poi va a richiamare l'oggetto con ID con valore inferiore al valore del lang impostato. Ci potrebbero essere altre soluzioni, ma questa permette di avere (vedremo successivamente), autocompletamenti multipli incrociati (per capirci se nel campo 1 metto A e nel campo 2 metto B nel campo C posso fare un autocompletamento in base a ciò che ho messo nel campo 1 e 2).

Per realizzare il campo autocompletante andiamo a creare il form nella maniera classica, ma andiamo ad aggiungere un paio di elementi aggiuntivi, in particolare il valore di autocomplete che permette di andare ad indicare il path di autocomplete, e oltra questo andiamo ad indicare alcuni attributi aggiuntivi che permetto di far funzionare l'autocomplletamento con dipendenza, in particolare viene aggiunta la classe dependence-autocomplete e a questo si aggiunge l'attributo lang che conterrà il valore dell'id dell'elemento padre da utilizzare, a cui poi si aggiunge anche l'attributo xml:lang che invece ha il compito di riportare il valore del lang dell'elemento al valore corretto (xml:lang ha la precedenza su lang).

Il form di autocompletamento viene così generato nel sseguente modo:

<?php
function funzione_genera_form() {
  global
$language;
 
$form =  array();
 
drupal_add_js(drupal_get_path('module','testautocomplete').'/js/script.js');
 
// First autocomplete
 
$form[0] = array(
   
'#type'          => 'textfield',
   
'#title'         => t('Frutto'),
   
'#description'   => t('Inserisci un frutto'),
   
'#maxlength'     => 128,
   
'#attributes'    => array('class' => 'dependence-autocomplete', 'lang' => 0, 'xml:lang' => $language->language),
   
'#autocomplete_path' => 'test/autocomplete',
  );
 
// Second autocomplete
 
$form[1] = array(
   
'#type'          => 'textfield',
   
'#title'         => t('Valore'),
   
'#description'   => t('Description'),
   
'#maxlength'     => 128,
   
'#attributes'    => array('class' => 'dependence-autocomplete', 'lang' => 1, 'xml:lang' => $language->language),
   
'#autocomplete_path' => 'test/autocomplete',
  );
  return
$form;
}
?>

A questo punto possiamo andare ad elaborare le informazioni di ricerca nella funzione apposita dell'autocompletamento, che oltre ad avere il classico parametro di ricerca, conterrà anche l'elemento che sta facendo al ricerca e l'eventuale testo presente nell'elemento padre.
Questa funzione può essere così definita:

<?php
/** Genera l'output necessario all'autocompletamento.
*
* @param $string
*   Stringa inserita che deve essere elaborata.
*
* @param $previous
*   Stringa contenente il termine padre di cui trovare i figli.
*
* @return
*   JSON data array
*/
function _test_autocomplete($string, $container, $previous = '') {
 
// Contenitore per i dati
 
$data = array();
 
/* ... */
  // Riporto l'array finale
 
return drupal_json($data);
}
?>

Potete trovare un esempio dell'autocompletamento in cascata, mentre trovate un modulo d'esempio allegato a questo articolo.

Per domande o chiarimenti postate pure tra i commenti.

AllegatoDimensione
Modulo di esempio per l'autocompletamento in cascata2.97 KB

Altri contenuti che potrebbero interessarti

  • Drupal si occupa esclusivamente della validazione lato server delle informazioni, ma spesso è consigliabile prevedere ad una prima validazione dei dati inseriti dall'utente lato client, in modo da evitare l'invio della richiesta limitando quindi le possibilità di errore. Ovviamente la validazione...
  • Esempio di vista generata dall'integrazione del nostro modulo con views
    Alzi la mano chi non ha mai usato views. Alzi la mano che l'ha mai usato. Ok, non proprio tutti ma buona parte di voi ha alzato la mano, questo perchè è sicuramente uno strumento potentissimo per la visualizzazione delle informazioni, quindi è fondamentale (o comunque molto utile) andare ad...
  • Pagina di ricerca con filtri dinamici nelle views
    Le viste sono uno strumento estremamente potente e permettono di andare ad elaborare le informazioni creando degli elementi (pagine, blocchi, ...) di presentazione. La possibilità di usare dei filtri esposti ne fa degli strumenti adatti alla creazione di strumenti di ricerca dei contenuti. L'...
  • Chi avesse iniziato ad utilizzare Drupal 7 si sarà senz'altro d'accordo che stanno nascendo dei temi di partenza molto sofisticati da cui partire per la realizzazione di temi, in particolare sta emergendo sempre più Omega 3. Questi temi, però possono avere necessità di essere estesi per aggiungere...

Commenti

Ritratto di luca

ciao ho letto il tuo articolo

ciao ho letto il tuo articolo da poco e l'ho provato, in particolare la modifica a Drupal.ACDB.prototype.search e mi funziona bene ma, non so perchè, mi fa una doppia chiamata.
ti è capitato qualche cosa del genere?

mi rendo conto che hai postato l'articolo un anno fa ma io lo leggo ora e mi sembra molto attuale ancora.

grazie
luca

Ritratto di mavimo

Ciao Luca,
potrebbe essere

Ciao Luca,
potrebbe essere che per qualche motivo non viene sovrascritta la funzione javascript, ma solo aggiunta a quelle in esecuzione. Potresti dirmi con che browser la stai provando? Hai a disposizione un ambiente di test per analizzarne il comportamento?

Ritratto di Anonimo

ciao se guardi l' esempio

ciao se guardi l' esempio http://mavimo.org/tutorial/multiple-autocomplete

qualsiasi lettera digiti ti appare sempre l'intera lista di stringhe presenti sul DB.

p.s. sto usando anch io XMLSITEMAP versione 1 per D6 ma mi ha creato solo un link nella sitemap. Puoi darci un' occhiatina ? Il sito è www.YouGeek.it

ciao :D

Ritratto di mavimo

per l'esempio lo so, la demo

per l'esempio lo so, la demo sul sito non funziona con il DB ma sono delle stringhe statiche, quindi funziona, solo che non l'ho istrutito per filtrare i dati da mostrare ;)

Per XmlSitemap dipende da troppi fattori che visti da utente normale non si possono capire.

Ritratto di Berlusconi

Ciao ho provato l'esempio ma

Ciao ho provato l'esempio ma non sembra funzionare :D

p.s. potresti dirmi che usi per creare la sitemap in modo automatico ?

Ritratto di mavimo

Che problema ti da? Puoi

Che problema ti da? Puoi darmi maggiori specifiche'

Per la sitemap uso XmlSitemap (versione 1 per Drupal 6)

Invia nuovo commento





  • Elementi HTML permessi: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <img> <h2> <h3> <h4> <table> <thead> <tbody> <tr> <th> <td>
  • You may post code using <code>...</code> (generic) or <?php ... ?> (highlighted PHP) tags.
  • Linee e paragrafi vanno a capo automaticamente.
  • Indirizzi web o e-mail vengono trasformati in link automaticamente

Maggiori informazioni sulle opzioni di formattazione.



Condividi contenuti