Actualizar el contenido de un select con ajax.

En esta entrada vamos a explicar como asociar dos selects mediante ajax y al modificar el elemento seleccionado en uno de ellos cambie el contenido del otro.

Podemos ver el ejemplo de funcionamiento aquí.

Un ejemplo muy claro para esta situación sería dos selects, uno con provincias y el otro con localidades, lo que queremos es que al cambiar de provincia varíe la lista de localidades y muestre las que están en la provincia seleccionada.

Lo haremos utilizando el ajaxHelper, para que no sea necesario la recarga de la página y quede más atractivo.

Lo primero que haremos será definir los modelos de provincias ,localidades y un tercer modelo en el que usaremos los selects, por ejemplo podría ser un modelo de cliente, en el que al insertar un nuevo cliente tendríamos que elegir la provincia y localidad a la que pertenece.

Modelos

Modelo de provincia.

/app/models/provincia.php

class Provincia extends AppModel
{
    var $name = 'Provincia';
}

Modelo de localidad.

/app/models/localidade.php

class Localidade extends AppModel
{
    var $name = 'localidade';
}

Modelo de cliente.

/app/models/cliente.php

class Cliente extends AppModel
{
    var $name = 'Cliente';
}

Controladores

Después de esto tendremos que definir los controladores.

Controlador de localidad.

/app/controllers/localidades_controller.php

class LocalidadesController extends AppController
{
	var $name = 'Localidades';
}

Controlador de provincia.

/app/controllers/provincias_controller.php

class ProvinciasController extends AppController
{
	var $name = 'Provincias';
}

Y por últino el controlador de clientes que será el que implementará la funcionalidad.

Controlador de cliente.

/app/controllers/clientes_controller.php

class ClientesController extends AppController
{
	var $name = 'Clientes';
	var $helpers = array('Ajax');
	var $uses = array('Cliente','Provincia','Localidade');

	function insertar()
	{
		$listadoProvincias = $this->Provincia->find('all', array('fields'=>array('id','nombre'),'order'=>'nombre ASC'));
		$this->set('provincias', Set::combine($listadoProvincias, "{n}.Provincia.id","{n}.Provincia.nombre"));
		$primera_provincia = $this->Provincia->find(null,null,'nombre ASC');
		$listadoLocalidades = $this->Localidade->find('all', array('fields'=>array('id','nombre'),'order'=>'nombre ASC','conditions'=>'Localidade.provincia_id='.$primera_provincia['Provincia']['id']));
		$this->set('localidades', Set::combine($listadoLocalidades, "{n}.Localidade.id","{n}.Localidade.nombre"));

		// RESTO DE LA FUNCIONALIDAD DE INSERCIÓN DE CLIENTES
	}

	function update_select()
	{
		if (!empty($this->data['Localidade']['provincia_id']))
		{
			$provincia_id = $this->data['Localidade']['provincia_id'];
			$localidades = $this->Localidade->find('all', array('fields'=>array('id','nombre'),'order'=>'nombre ASC','conditions'=>array('provincia_id'=>$provincia_id)));
		}
		else
		{
			$localidades = $this->Localidade->find('all', array('fields'=>array('id','nombre'),'order'=>'nombre ASC'));
		}
		$this->set('options', Set::combine($localidades, "{n}.Localidade.id","{n}.Localidade.nombre"));
		$this->render('/elements/update_select', 'ajax');
	}
}

Por un lado tenemos la función insertar, que será una función genérica de inserción de clientes. La parte que a nosotros nos interesa es en la que se inicializan las variables que después utilizaremos en los selects, estas deberán ser arrays en los que cada elemento tenda un identificador y su valor, para que el select pueda utilizarlos en la vista.

En la versión 1.1 de cakephp, esto se podía hacer mediante la función generateList del modelo, función que se ha eliminado en la 1.2.
Por ello ahora es necesario hacerlo en dos pasos, por un lado realizar la búsqueda con un findAll y después utilizar la función combine de la clase Set que genera un array con la estructura deseada, que será el que le pasemos a la vista.

$listadoProvincias = $this->Provincia->find('all', array('fields'=>array('id','nombre'),'order'=>'nombre ASC'));
$this->set('provincias', Set::combine($listadoProvincias, "{n}.Provincia.id","{n}.Provincia.nombre"));

La segunda función update_select, es a la que llamará el ajax para actualizar el listado de localidades a partir de un identificador de provincia.

Como se puede ver en el código, la función coge el identificador de provincia del select y envía a la vista los arrays actualizados de provincias y localidades.

Vistas

Localidades y provincias no tendrán vistas asociadas, ya que no hay ninguna operación en el controlador.

En clientes tendremos que crear la vista para la operación insertar del controlador, esta podría ser algo como esto:

/app/views/clientes/insertar.ctp

echo $form->create('Inscripcione',array('action'=>'insertar'));
echo $form->inputs(array('legend'=>'Actualizar Provincias',
		'Localidade.provincia_id' => array('label'=> 'Provincia','showEmpty'=>'false','id'=>'provincias'),
		'Alumno.localidade_id' => array('label'=> 'Localidad','showEmpty'=>'false','id'=>'localidades'),
			));
echo $form->end();

$options = array('url' => 'update_select','update' => 'localidades');
echo $ajax->observeField('provincias',$options);

La vista será muy sencilla, un formulario con dos selects, uno de localidades y otro de provincias y una llamada al ajaxHelper en la que se indica que cada vez que se modifique el valor del select provincias, se llame a la función update_select y actualice el select de localidades.

Este select se actualizará con los valores devueltos por la vista de update_select, que lo único que hace es imprimir todos los nombres de localidades devueltos por la función del controlador.

/app/views/elements/update_select.ctp
if(!empty($options)) {
  foreach($options as $k => $v) {
    echo "<option value='$k'>$v</option>";
  }
 }

En este enlace se puede ver un ejemplo de funcionamiento.

4 Responses to “Actualizar el contenido de un select con ajax.”

  1. Francisco Villavicencio writes:

    Hola:
    Corrí los dos ejemplos de provincias y localidaes y no encuentro diferencia alguna. las localidades no están cambiando al elegir una nueva provincia, como es el la idea.
    Voy a trabajar en esto.

  2. bernal writes:

    La idea es que el listado de categorías varíe al modificar la provincia, es decir, que al seleccionar una nueva provincia en el listado correspondiente, se muestren en el listado de localidades únicamente las que pertenecen a esa provincia.

    Espero haber respondido tu duda, un saludo.

  3. jm writes:

    Bueno pues he estado tratando de usar tu ejemplo y luego de aplicarlo el evento de update ocurre pero no asi el cambio de valores en el combo, la verdad me parece que el problema q tengo esta en el punto de la vista, no estoy muy seguro que tendria que devolver update_select en este caso para que sean datos validos para poblar un select

  4. bernal writes:

    Francisco y jm, debo pediros disculpa, revisando el código de la vista update_select me he dado cuenta de que tenía un error, era normal que no os funcionase.
    Al insertar código html en la entrada, wordpress lo trataba como tal y no lo mostraba como texto, lo he corregido y el nuevo código debería funcionar sin problemas.

    Disculpas de nuevo y un saludo

Escribir una respuesta