Supporto volontario e collaborativo per Joomla!® in italiano

Select list dipendenti con Joomla e Mootools

Informazioni utiliMolte volte abbiamo due select list legate fra di loro: al variare della prima, devono essere caricati valori differenti nella seconda; un esempio pratico è il caso delle province - comuni.
Con un po' di codice javascript e qualche riga di PHP possiamo ottenere un risultato efficiente e veloce.



Passiamo subito ai fatti.
Io avevo l'esigenza di creare select list dipendenti, come nel classico caso di province->comuni.
In più, il codice che le generava doveva essere dinamico a sua volta, perchè i campi cambiavano nome a seconda di dove mi trovavo.

Il risultato che volevo ottenere (ed ho ottenuto) è stato questo:



Il sistema si compone di 3 parti:
  • la funzione che costruisce al volo il codice javascript (chiamata JSlinkedSelect)
  • la funzione che elabora la richiesta Ajax e resituisce i valori per la select dipendente (chiamata json_select, dentro un controller chiamato helper)
  • il codice necessario per far partire il tutto

Il codice javascript

Partiamo dalla generazione del codice javascript; anche in questo caso possiamo dividerlo in due:
  • una parte generale che si occuperà di processare e inserire i nuovi valori nella select dipendente
  • la parte ajax vera e propria, inclusa in una seconda funzione

JSlinkedSelect si aspetta un array di array, perchè in una pagina potrei avere più di una select list dipendente.
Le chiavi dell'array "più interno" sono le seguenti:
$ele['padre']   = elemento "padre". al cambiare del suo valore il figlio caricherà i valori legati
$ele['figlio']     = elemento "figlio".
$ele['table']    = tabella dove si trovano i valori per il figlio.
$ele['where']  = campo di collegamento (join) fra il padre e il figlio.

function JSlinkedSelect($select){
$js = "window.addEvent('domready', function(){
var base_url = 'index.php?option=com_miocomponente&controller=helper&task=json_select&format=raw';

function buildSelect(select, options)
{
var select = $(select);
select.empty();
options.each(function(item) {
var option = new Element('option', {
value: item.value.toString()
});
option.setHTML(item.text.toString());
option.injectInside(select);
});
}";

foreach($select as $ele){
$js .= buildAddEvent($ele);
}

$js .= "});";

return $js;
}
Di questa funzione non c'è molto da dire; viene creata il codice JS che si occuperà di "svuotare" il figlio e di inserire le opzioni prendendole dalla risposta in formato JSON.
Il grosso del lavoro viene svolto da buildAddEvent()
function _buildAddEvent($ele){
$js =
"$('{$ele['padre']}').addEvent('change', function(){
if($('{$ele['figlio']}_wait')) $('{$ele['figlio']}_wait').setStyle('display', 'inline');
var req_url = base_url + '&id=' + this.value + '&table={$ele['table']}&where={$ele['where']}';
var JSonReq = new Json.Remote(req_url, {
onComplete: function(response){
buildSelect('{$ele['figlio']}', response);
if($('{$ele['figlio']}_wait')) $('{$ele['figlio']}_wait').setStyle('display', 'none');
}
}).send();
});";

return $js;
}

Ok, facciamo un po' di chiarezza.
Ogni volta che il padre cambierà valore, viene lanciata la relativa funzione.
Innanzi tutto, se presente, viene mostrata la classica immagine di attesa, che deve essere inserita direttamente nel codice HTML in questo modo:

img src="/images/wait16trans.gif" border="0" style="display: none"
Se devo essere sincero questa non è la soluzione "più elegante", però al momento... :D

Dopodichè vengono composti i parametri della richiesta, quando verrà completata, la risposta sarà processata e inserita nella select figlia.
Andiamo a vedere come viene elaborata la richiesta

Il codice PHP


Nel nostro controller ho creato un task che si occupa di gestire la chiamata e restituire i valori richiesti nel formato JSON.

function json_select(){
$id      = JRequest::getCmd('id');
$join  = JRequest::getCmd('where');
$table = JRequest::getCmd('table');

if((int)$id) $where = " $join = $id";
else       $where = " $join = ".$this->_db->Quote($this->_db->getEscaped($id, true), false);   #escape + quote della stringa

$query = "SELECT id as id, descr as descr FROM #__{$table} WHERE 1 = 1";
if($where)$query .= " AND $where";      
$query .= " ORDER BY 2";

$this->_db->setQuery($query);
$rows = $this->_db->loadObjectList();

$response[] = array('value' => '', 'text' => ' - Seleziona - ');
foreach($rows as $row){
$t['value'] = $row->id;
$t['text'] = $row->descr;
$response[] = $t;
}

echo json_encode($response);
}

Come prima cosa prendo i valori necessari per costruirmi la select (id, campo di join e la tabella interessata). L'id può essere numerico oppure stringa, per cui in quest'ultimo caso effettuo l'escape e il quote. Credo sia sufficiente, ma sono aperto a suggerimenti :)
La query si basa su una convenzione personale, in cui tutti i campi "id" si chiamano.. id e tutti i campi di descrizione... descr :D
Una volta caricata la query, organizzo i dati nello standard di Joomla e poi restituisco il tutto a video.

Pronti? Via!

Infine, questo è il modo in cui viene chiamato il tutto:
$select[] = array('padre' => 'provincia', 'where' => 'provincia', 'table' => 'comuni', 'figlio' => 'comune'    );
$select[] = array('padre' => 'prov_nasc', 'where' => 'provincia', 'table' => 'comuni', 'figlio' => 'comune_nasc');

$js = $helper->JSlinkedSelect($select);

Questa serie di funzioni restituisce tutto il codice javascript necessario per avere due select list dipendenti, dopodichè è sufficiente aggiungerlo al documento tramite la funzione

$doc =& JFactory::getDocument();
$doc->addScriptDeclaration($js);

Alcune considerazioni:
  • il formata da utilizzare deve essere quello raw, altrimenti Joomla effettua l'output anche di altro codice che non ci interessa (file javascript, css, metatag ecc ecc); per cui deve essere creata la view corrispondente.
  • la query si basa su un convenzione personale, spero che anche voi adottiate un vostro standard interno, altrimenti... si può fare ma la vedo un po più dura
  • le funzioni javascript sono state sviluppate per il framework mootools 1.11, che fino a poco tempo fa è stato lo standard su tutte le installazioni di Joomla. Ho fatto delle prove con il plugin della nuova versione attivo e mi sembra che funzioni tutto lo stesso

FabbricaBinaria - Soluzioni originali ad esigenze quotidiane
CommentaCommenta questo articolo nel forum