Hola a todos,
Les comparto esta pequeña librería que hice para hacer Selects Dependientes de N niveles con AJAX.
Su uso es bastante simple, solo es instanciar el select y pasar quien es el que depende y el URL para cargar los datos, posteriormente en ese URL van a recibir por GET dos datos, controlName (el nombre del control) y selectedId (el valor que selecciono).
La clase espera recibir un arreglo codificado en JSON, donde cada elemento es un objeto, con una propiedad text, y otra value, de esta forma:
Código:
[
{ text: "Texto a mostrar", value: "valor del option" },
{ text: "Texto 2", value: "valor2" },
....
{ text: "Texto n", value: "ValorN" }
]
La forma de uso es bastante simple, basta con tener un HTML:
Código HTML:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script type="text/javascript" src="prototype.js"></script>
<script type="text/javascript" src="dependant.js"></script>
<title>Demo Selects Dependientes</title>
</head>
<body>
<select name="sel1" id="sel1">
<option value="">- Selecciona -</option>
<option>Mascotas</option>
<option>Carros</option>
</select>
<select name="sel2" id="sel2"><option></option></select>
<select name="sel3" id="sel3"><option></option></select>
<script type="text/javascript">
Event.observe(window, 'load', function() {
var sel3 = new HTMLSelect( 'sel3' );
var sel2 = new dependantSelectAJAX( 'sel2', sel3, 'loader.php' );
var sel1 = new dependantSelectAJAX( 'sel1', sel2, 'loader.php' );
});
</script>
</body>
</html>
Creamos los Selects en el evento load de la ventana, y estos objetos automáticamente registran cuando haces un cambio en cada select.
Dejo un pequeño ejemplo de como cargar los datos via PHP, aunque es independiente del lenguaje, el único requisito es que los datos vengan en JSON y con la estructura mencionada mas arriba:
Código PHP:
<?php
$mascotas = array( array( "text" => "Gato", "value" => "cat" ), array( "text" => "Perro", "value" => "dog" ), array( "text" => "Pajaro", "value" => "bird" ) );
$carros = array( array( "text" => "BMW", "value" => "bmw" ), array( "text" => "Lamborghini", "value" => "lamb" ), array( "text" => "Ferrari", "value" => "ferr" ) );
$mascotas_acc_cat = array( array( "text" => "Collar" ), array( "text" => "Estambre" ), array( "text" => "Arena" ) );
$mascotas_acc_dog = array( array( "text" => "Collar" ), array( "text" => "Correa" ), array( "text" => "Pelota" ) );
$mascotas_acc_bird = array( array( "text" => "Jaula" ), array( "text" => "Alpiste" ) );
$carros_color_bmw = array( array( "text" => "Azul" ), array( "text" => "Negro" ) );
$carros_color_lamb = array( array( "text" => "Azul" ), array( "text" => "Amarillo" ), array( "text" => "Rojo" ) );
$carros_color_ferr = array( array( "text" => "Rojo" ) );
switch( $_GET['controlName'] ) {
case 'sel1':
switch( $_GET['selectedId'] ) {
case 'Mascotas':
$result = $mascotas;
break;
case 'Carros':
$result = $carros;
break;
}
break;
case 'sel2':
switch( $_GET['selectedId'] ) {
case 'cat':
$result = $mascotas_acc_cat;
break;
case 'dog':
$result = $mascotas_acc_dog;
break;
case 'bird':
$result = $mascotas_acc_bird;
break;
case 'bmw':
$result = $carros_color_bmw;
break;
case 'lamb':
$result = $carros_color_lamb;
break;
case 'ferr':
$result = $carros_color_ferr;
break;
}
}
echo json_encode( $result );
exit();
?>
Finalmente la clase dependant.js que es necesaria para funcionar todo, le falta documentación pero leyendo un poco el código podrán ver los métodos que ocupa:
Código:
var DataStore = Class.create(Enumerable, {
initialize: function( store ) {
if( store == null ) {
store = [];
}
this._store = store;
},
addItem: function( text, value ) {
if( value == null ) {
value = text;
}
this._store.push( {text: text, value: value } );
},
removeItem: function( idx ) {
this._store.splice( idx, 1 );
},
clear: function() {
this._store = [];
},
size: function() {
return this._store.length;
},
inspect: function() {
alert( this._store.inspect() );
},
_each: function(iterator) {
for (var i = 0; i < this._store.length; i++) {
var value = this._store[i];
iterator(value);
}
}
});
var HTMLSelect = Class.create({
initialize: function( element ) {
this.element = $(element);
this.element.onchange = this.onChange.bindAsEventListener(this);
this.element.onclick = this.onClick.bindAsEventListener(this);
this.element.onfocus = this.onFocus.bindAsEventListener(this);
var store = new DataStore();
var opts = this.element.options;
for(var i = 0; i < opts.length; i++ ) {
var el = opts[i];
store.addItem( el.text, el.value );
}
this.store = store;
},
setStore: function( ds ) {
this.store = ds;
},
reload: function() {
this.empty();
var num = 0;
this.store.each(function(item) {
this.addOption(item.text, item.value);
num++;
}.bind(this));
},
onChange: function(e) {},
onClick: function(e) {},
onFocus: function(e) {},
onEmpty: function() { return true; },
selectIndex: function( index ) {
this.element.selectedIndex = index;
},
selectOption: function( option ) {
var size = this.element.length;
var found = false;
for(i = 0; i < size; i++) {
var el = this.element.options[i].text;
if( el == option ) {
found = true;
break;
}
}
if( found ) {
this.selectIndex(i);
this.onChange();
}
},
countOptions: function() {
return this.element.length;
},
getSelectedOption: function() {
var op = this.element.options[this.element.selectedIndex];
return { value: op.value, text: op.text };
},
getValue: function() {
var op = this.element.options[this.element.selectedIndex];
var ret = "";
ret = op.value;
if( ret == "" ) {
ret = op.text;
}
return ret;
},
empty: function() {
if( this.onEmpty() ) {
this._empty();
}
},
addOption: function( text, value ) {
if( value == null ) {
value = text;
}
var op = new Option( text, value );
var idx = this.element.length;
this.element.options[idx] = op;
return idx;
},
deleteOption: function( index ) {
if( this.element.length > 0 && index > 0 ) {
this.element.options[index] = null;
}
},
selectAllOptions: function() {
var size = this.element.length - 1;
for(i = size; i>=0; i--) {
this.element.options[i].selected = true;
}
},
getSelectedOptions: function() {
var texts = [];
var size = this.element.length - 1;
for(i = size; i>=0; i--) {
if( this.element.options[i].selected === true ) {
texts.push(this.element.options[i].text);
}
}
return texts;
},
_empty: function() {
this.element.options.length = 0;
}
});
var dependantSelectAJAX = Class.create(HTMLSelect, {
initialize: function( $super, select, child, url ) {
$super( select );
if( typeof( select ) == "string" ) {
this.name = select;
} else {
this.name = select.name;
}
this.child = child;
this.url = url;
},
onChange: function(e) {
this.child.empty();
var value = this.getValue();
if( value != "" ) {
var request = new Ajax.Request( this.url, {
method: 'get',
parameters: {controlName: this.name, selectedId: value},
onSuccess: function( transport ) {
var store = transport.responseText.evalJSON(true);
if( typeof store.error != "undefined" ) {
alert( store.error );
} else {
this.child.setStore(new DataStore(store));
this.child.reload();
var size = this.child.countOptions();
if( size == 1 ) {
this.child.onChange();
}
}
}.bind(this),
onFalure: function(t) {
alert( "Error in request" );
}
});
}
},
onEmpty: function() {
this.child.empty();
return true;
}
});
Requiere de la libreria Prototype versión 1.6 minimo debido a la nueva forma de declarar clases.
Saludos y espero les sea de utilidad.