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){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.
$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;
}
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
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' );Questa serie di funzioni restituisce tutto il codice javascript necessario per avere due select list dipendenti, dopodichè è sufficiente aggiungerlo al documento tramite la funzione
$select[] = array('padre' => 'prov_nasc', 'where' => 'provincia', 'table' => 'comuni', 'figlio' => 'comune_nasc');
$js = $helper->JSlinkedSelect($select);
$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
Commenta questo articolo nel forum