La Página de DriverOp

Listas dependientes, segunda parte.

En este artículo volveré a abordar el problema de hacer que dos listas tipo select (o listbox) sea dependiente el segundo respecto del primero, es decir que el segundo select cambie de valores dependiendo del valor seleccionado en el primero, pero esta vez implementado con Ajax de JQuery y base de datos.

Introducción.

En el artículo original implementé una solución que se basa en que la fuente de datos para los selects es un archivo de texto y la ejecución es sincrónica. En este artículo se verá una solución diferente. La implementación se hará usando Ajax de JQuery y la funte de datos será una base de datos, todo con programación en PHP del lado del servidor.

Tal como en el artículo original, en esta la solución se usará como ejemplo un selector de países y otro de regiones o províncias que permitirá al visitante seleccionarla según el país que haya elejido en el primero.

La mecánica será como sigue: El select con los países se cargará directamente de la base de datos, el select de regiones se cargará mediante Ajax también usando la base de datos aprovechando la implementación que JQuery ofrece.

He elejido JQuery por ser simple de usar y porque su uso está suficientemente extendido además de estar muy bien documentado (si se sabe inglés, claro).

Como la implementación requiere de base de datos, lo primero que hay que hacer es construir las tablas.

Tablas países y regiones:

A continuación las tablas "paises" y "regiones":

paises
idnombre
1Argentina
2Colombia
3México
regiones
idid_paisnombre
11Buenos Aires
21Córdoba
31Entre Ríos
42Distrito Capital
52Atlántico
62Antioquía
73Distrito Federal
83Michoacán
93Monterrey

Esto no requiere mucha explicación. Las tablas contienen lo mínimo necesario y sirven nada más que como ejemplo. El campo "id_pais" de la tabla "regiones" es el índice de la tabla "paises" al cual la región pertenece.

El código HTML y PHP

.

La página HTML donde se implementará esto debe ser como mínimo de esta forma:

<!doctype HTML>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<p>País: <select id="selector1" name="pais"></select></p>
<p>Región: <span id="sel2"></span></p>
</body>
</html>

El <select> "selector1" está vacío por ahora mientras que no hay un segundo select, el por qué lo explicaré más adelante.

Teniendo las tablas y el esqueleto HTML vamos a proceder a escribir el código PHP que llenará el primer select "selector1".

<?php
	$link = mysql_connect("localhost", "myusername", "mypassword", true); // <-- debes cambiar por tus datos de acceso a MySQL.
	mysql_select_db("test", $link); // <-- debes cambiar "test" por el nombre de tu base de datos en MySQL.
	mysql_query("SET NAMES UTF8",$link);
	
	$sql = "SELECT * FROM `paises` ORDER BY `nombre`";
	
	$result = mysql_query($sql,$link);
	
	
?>
<!doctype HTML>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<p>País: <select id="selector1" name="pais">
<?php
	while ($fila = mysql_fetch_assoc($result)) {
		echo sprintf('<option value="%s">%s</option>',$fila['id'],$fila['nombre']);
	}
?>
</select></p>
<p>Región: <span id="sel2"></span></p>
</body>
</html>
<?php
	mysql_close($link); // Nunca olvides cerrar la conexión a la base de datos.
?>

Esto generará efectivamente las opciones del primer select con los países.

Como la implementación requiere una forma de generar dinámicamente el segundo select y esto deberá hacerse mediante una petición Ajax, hay que escribir un script PHP que haga justamente esto el cual será el objetivo de la petición Ajax.

Este script no hará más que escribir un option por cada región que haya en la tabla "regiones" con una salvedad, solamente lo hará dependiendo de un valor que se pasa por $_GET que será el índica "id" del país cuyas regiones se quieren mostrar. A este script lo llamaré "makeselect2.php". El código es el siguiente:

<?php
	
	$idpais = $_GET['pais'];
	
	$link = mysql_connect("localhost", "myusername", "mypassword", true); // <-- debes cambiar por tus datos de acceso a MySQL.
	mysql_select_db("test", $link); // <-- debes cambiar "test" por el nombre de tu base de datos en MySQL.
	mysql_query("SET NAMES UTF8",$link);
	
	$sql = "SELECT * FROM `regiones` WHERE `id_pais` = ".mysql_real_escape_string($idpais)." ORDER BY `nombre`";
	
	$result = mysql_query($sql,$link);
	
	echo '<select id="selector2" name="region">';
	while ($fila = mysql_fetch_assoc($result)) {
		echo sprintf('<option value="%s">%s</option>',$fila['id'],$fila['nombre']);
	}
	echo '</select>';
	mysql_close($link);
?>

El parámetro en $_GET se llamará convenientemente "pais". Ese dato lo usamos al construir la sentencia SQL haciéndola pasar por la función mysql_real_escape_string() para evitar (o más bien aliviar) los ataques por SQL Injection.

Podrás notar además que estoy ejecutando una sentencia SQL algo extraña inmediatamente después de hacer la conexión: SET NAMES UTF8. Lo que esto hace es establecer la conexión a la base de datos en la tabla de caracteres UTF8, esto sirve para que los caracteres especiales no-ASCII se impriman correctamente en la página (especialmente las vocales asentuadas y las "ñ" en español).

Entra JQuery.

Bien pues, tenemos todo listo para hacer que las cosas funcionen.

El lector podría pensar que se puede generar el sergundo select "region" directamente en el código de la página pero dos razones atentan contra esto. La primera es metodológica. Aunque se haga esto, se requerirá del script "makeselect2.php" de todos modos con la consecuencia de que se estará repitiendo código. La otra es práctica. Quien sepa JQuery sabrá que se puede ejecutar la petición Ajax inmediatamente después de que la página se cargue haciendo uso del evento .ready() de JQuery. De esta forma no se repite código y tiene el agregado de que podemos establecer el valor por omisión del select "selector1" a la opción que queramos sin necesidad de cambiar nada más en el resto del código.

Al código HTML ya visto vamos a agregar la sección <script> en el <head> que hará lo que acabo de comentar.

<script src="jquery-1.7.1.min.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(
	function () {
		$("#sel2").load("makeselect2.php?pais="+$("#selector1").val());
		$("#selector1").change(
			function () {
				$("#sel2").load("makeselect2.php?pais="+$("#selector1").val());
			}
		);
	}
);
</script>

Primero se carga la biblioteca JQuery. Luego ejecutamos .ready() en el objeto document. Sobre el elemento "sel2" (que es un <span> en el código HTML) ejecutamos la petición Ajax que en JQuery es el método .load() al cual le pasamos como parámetro la dirección del script "makeselect2.php" adosándole como parámetro GET "pais" el valor actual del select "selector1" mediante la llamada al método .val() que nos proporciona JQuery.

El metodo .load() de JQuery aplicado sobre un elemento funciona rellenando ese elemento con el resultado que el servidor envia al llamar al script correspondiente.

Y aquí tengo que hacer una aclaración. Parece poco intuitivo que haya usando un <span> como contenedor del select y que además "makeselect2.php" genere todo el tag <select> cuando lo obvio sería poner el segundo select como parte del esqueleto HTML y usar su id en la llamada a .load(). Pero hay una razón para ello: Internet Explorer.

Este navegador tiene un bug. Un error que hace que se ignore la asignación de la propiedad .innerHTML cuando el elemento es un <select>. De forma tal que hacer esto:

	document.getElementById("selector2").innerHTML = "<option value=1>Valor uno</option>";

No funciona.

Internet Explorer simplemente no hace nada con "selector2" cuando es un elemento <select>. Esa es la razón de que se use un <span> como contenedor del <select> "selector2" y que "makeselect2.php" genere no solo los <option> sino todo el tag <select> completo, con su "name" y "id".

A continuación de la llamada Ajax, tomamos el <select> "selector1" y le asignamos un manejador a su evento onChange. El código de este evento no es ni más ni menos que el mismo de antes. Este código es el que se ejecutará cada vez que el visitante cambie de opción en el <select> de paises, provocando que "selector2" se reemplace con las regiones que se corresponden con el país seleccionado.

Eso es todo. Puedes ver todo esto funcionando .

Descargas:

Por Diego Romero,