Autocompletar ajax

En la entrada semanal de nuestro blog relacionado con cakephp, vamos a hablar sobre autocompletar los campos mediante ajax, una opción que puede simplificar bastante la inserción de datos por parte de los usuarios y que a su vez resulta bastante elegante.

El funcionamiento es sencillo, al introducir una letra en un campo de entrada se buscarán en la base de datos las coincidencias con el texto que hemos insertado, mostrándolas como una lista y dándonos la opción de elegir entre todos los resultados.

Esta opción se usa habitualmente con los listados de paises, en nuestro ejemplo utilizaremos, como en el ejemplo de actualizar un select con ajax, las provincias españolas.

En este enlace se puede comprobar el funcionamiento.

Añadirlo en nuestro proyecto es realmente sencillo, supongamos que tenemos un modelo, cuyo controlador tiene una operación y una vista asociada, que será donde queremos utilizarlo.

Para ello en la vista, añadimos este trozo de código dentro de un formulario:

 echo $form->label('provincias','Provincias');
 echo $ajax->autoComplete('nombre','autocomplete');

Añadimos un label que tendrá el texto Provincias y un campo de entrada autocomplete, este recibe como parámetros, el nombre del campo y como segundo parámetro la url donde se encuentra la función a la que se va a llamar cuando se introduzca una letra, en nuestro caso, la vamos a añadir en el mismo controlador que estamos utilizando, si no fuese así la dirección sería ‘/nombre_del_controlador/función’.

En segundo lugar, creamos la función autocomplete, dentro del controlador utilizado añadimos el siguiente código:

  function autocomplete()
  {
 	$str = trim($this->data['Provincia']['nombre']);
	$str = str_replace("á","a",$str);
	$str = str_replace("é","e",$str);
	$str = str_replace("í","i",$str);
	$str = str_replace("ó","o",$str);
	$str = str_replace("ú","u",$str);
	$str = str_replace("Á","A",$str);
	$str = str_replace("É","E",$str);
	$str = str_replace("Í","I",$str);
	$str = str_replace("Ó","O",$str);
	$str = str_replace("Ú","U",$str);
	$str = str_replace("Ñ","ñ",$str);
	$condicion = "REPLACE (REPLACE( REPLACE( REPLACE( REPLACE( REPLACE ( Provincia.nombre,'á','a'), 'é','e'),'í','i'),'ó','o'),'ú','u'),'Ñ','ñ') LIKE "$str%"";
	$this->set('provincias', Set::combine($this->Provincia->findAll($condicion,array('Provincia.nombre','Provincia.id'),'Provincia.nombre'), "{n}.Provincia.id","{n}.Provincia.nombre"));
	$this->render('autocomplete','ajax');
 }

La función es muy sencilla, realiza la búsqueda en la base de datos de todas las provincias cuyo nombre coincida con el texto introducido, pasado en $this->data con el nombre que se indica en el primer parámetro de la función autocomplete utilizada en la vista, para eso se añade en la condición la opción LIKE cadena% de sql.

Todas las opciones que añadimos anteriormente sirven para ignorar los acentos, tanto en la cadena que introducimos como en los nombres de las provincias almacenadas en la base de datos, con esto al realizar la búsqueda funcionará aunque cometamos fallos ortográficos.

Crearemos una vista para esta función, con una única línea de código, que crea una lista con todas las entradas del array.

 echo $html->nestedList($provincias);

Y por último añadiremos al css que estemos utilizando, dos clases que modificarán el aspecto del listado que hemos creado:

  div.auto_complete {
 	position :absolute;
 	width :250px;
 	background-color :white;
 	border :1px solid #888;
 	margin :0px;
 	padding :0px;
  }

 div.auto_complete ul{
	list-style: none;
	margin: 0px;
 }

 li.selected{
	background-color: #ffb;
 }

Con esto ya tendremos un campo de texto en el que al añadir texto se generará un listado con todas las entradas de la base de datos que coincidan con el texto introducido.

17 Respuestas a “Autocompletar ajax”

  1. bernal escribió:

    Prueba a cambiar la línea:
    $this->set(’localidades’, set::combine($this->Localidade->findAll($condicion,array(’Localidade.nombre’,’Localidade.id’),’Localidade.nombre’), “{n}.Localidade.id”,”{n}.Localidade.nombre”));

    Por:
    $this->set(’localidades’, Set::combine($this->Localidade->find(’all’,array(’conditions’=>$condicion,’fields’=>array(’Localidade.nombre’,'Localidade.id’),’order’=>’Localidade.nombre DESC’)), “{n}.Localidade.id”,”{n}.Localidade.nombre”));

    No entiendo muy bien porqué falla, está haciendo bien la consulta, pero está generando mal el array de localidades.

    ¿Qué versión de cakephp estás usando?

  2. Nelli escribió:

    He probado y no funcciona, $localidades no tiene valor.
    Uso Cakephp 1.2

  3. bernal escribió:

    No se me ocurre qué decirte. La única idea que tengo es que me mandes un enlace del ejemplo que estás haciendo o el código de la aplicación para intentar buscar el error.

  4. Nelli escribió:

    He probado generar array localidades sin usar Set::combine, porque allí es donde fallaba.

    Entonces ahora creo un array $loc donde este el listado de localidades (el resultado de la búsqueda).

    Resulta que las busca bien , pero no sé porque, en el listado muestra solo la primera letra. O sea que cuando hago
    $loc[]=$v2["nombre"];
    asigna solo la primera letra. Sabes porque pasa eso?
    Aquí está el codigo.
    Gracias

    $condicion = “REPLACE (REPLACE( REPLACE( REPLACE( REPLACE( REPLACE ( Localidade.nombre,’á’,'a’), ‘é’,'e’),’í’,'i’),’ó’,'o’),’ú’,'u’),’Ñ’,'ñ’) LIKE ‘$str%’”;
    $localidades1=$this->Localidade->findAll($condicion,array(’Localidade.nombre’),’Localidade.nombre’);

    foreach($localidades1 as $key=>$value)
    {

    foreach($value as $k=>$v)
    {
    foreach($v as $k2=>$v2)
    {
    $loc[]=$v2["nombre"];
    }
    }
    }

    $this->set(’localidades’, $loc);
    $this->render(’autocomplete_loc’,'ajax’);

  5. Nelli escribió:

    Hola bernal,

    Como no funcciona con Set::combine al final he encontrado el modo de hacerlo a mano. O sea que gererar array a mano:

    function autocomplete_loc() {

    $str = trim($this->data['Empresa']['localidad']);
    $str = str_replace(”á”,”a”,$str);
    $str = str_replace(”é”,”e”,$str);
    $str = str_replace(”í”,”i”,$str);
    $str = str_replace(”ó”,”o”,$str);
    $str = str_replace(”ú”,”u”,$str);
    $str = str_replace(”Á”,”A”,$str);
    $str = str_replace(”É”,”E”,$str);
    $str = str_replace(”Í”,”I”,$str);
    $str = str_replace(”Ó”,”O”,$str);
    $str = str_replace(”Ú”,”U”,$str);
    $str = str_replace(”Ñ”,”ñ”,$str);

    $condicion = “REPLACE (REPLACE( REPLACE( REPLACE( REPLACE( REPLACE ( Localidade.nombre,’á’,'a’), ‘é’,'e’),’í’,'i’),’ó’,'o’),’ú’,'u’),’Ñ’,'ñ’) LIKE ‘$str%’”;
    $localidades1=$this->Localidade->findAll($condicion,array(’Localidade.nombre’),’Localidade.nombre’);

    foreach($localidades1 as $key=>$value)
    {

    foreach($value as $k=>$v)
    {

    $loc[]=$v["nombre"];
    }
    }

    $this->set(’localidades’, $loc);
    $this->render(’autocomplete_loc’,'ajax’);

    }

    Así funcciona.

    Muchas gracias por todo.
    Saludos

  6. bernal escribió:

    Me alegro de que hayas solucionado el problema, a mi no se me ocurría donde podía estar el fallo. De todas formas, que no funcione set::combine es muy raro, a nosotros nos funciona perfectamente, igual es debido a que utilizas una versión del cakephp 1.2, que no es la final y tenían algún bug con esa clase, aunque me parece bastante raro.

    Un saludo y muchas gracias por visitarnos.

    Please continue discussion on the forum: link

[ bbPress synchronization by bobrik ]