Autocompletador con Prototype/Scriptaculous – Parte I

Prototype, al igual que otros frameworks de JavaScript, permite implementar fácilmente comportamientos que que serían dispendiosos de crear a partir del JavaScript básico y convencional.  El hermanito Scriptaculous viene a ayudarnos aún mas con sus efectos y ayudantes.

Un ejemplo de estas posibilidades de rápida implementación es la simulación del autocompletar de un campo de texto.

El objeto Autocompleter acepta dos fuentes de datos diferentes.

  1. Una local como un arreglo de JavaScript.
  2. Una remota y mas dinámica a través de solicitudes AJAX.

En este artículo se va a realizar la demostración del uso de un Autocompleter.local.  Para esto se requieren dos componentes.

  • Un campo de texto donde el usuario escribe el criterio de búsqueda.
  • Una sección de sugerencias donde el Autocompleter despliega las coincidencias basadas en el criterio de búsqueda.
Ejemplo de Autocompleter.local
Ejemplo de Autocompleter.local

Para el ejemplo anterior cuenta con los siguientes elementos.

  • t_criterio: campo de texto para el criterio de búsqueda.
  • resultado: es el div donde se  desplegarán las sugerencias encontradas.
  • departamentos: es un arreglo JavaScript con la información local y estática para las opciones de búsqueda, en este caso el listado de los departamentos de Colombia.

Para activar el autocompletado sobre t_criterio basado en los valores de departamentos, se ejecuta la siguiente instrucción JavaScript.

var ac = new Autocompleter.Local('t_criterio',
                 'resultado',
                 departamentos,
                 {
                     autoSelect     : false,
                     frequency      : 0.4,
                     minChars       : 1,
                     choices        : 10,
                     paritialSearch : false,
                     paritialChars  : 2,
                     fullSearch     : true,
                     ignoreCase     : true
                 });

Los parámetros especificados al método Local son descritos a continuación.

  1. Id del campo de texto con el criterio de búsqueda.
  2. Id del contenedor de los resultados obtenidos.
  3. Arreglo con la información disponible.
  4. Hash con las opciones de configuración del autocompletador.  Todos estos valores son opcionales y los mostrados corresponden con los valores por defecto.

A continuación se describen las opciones de configuración disponibles.

  • autoSelect: Acepta automáticamente la primera sugerencia cuando sólo hay una.
  • frecuency: Tiempo en segundos entre dos intentos de autocompletar.
  • minChars: Cantidad mínima de carácteres necesarios para que se active el autocompletar.
  • choices: Cantidad máxima de opciones sugeridas a mostrarse.
  • paritialSearch: Tomar en cuenta para la búsqueda el comienzo de cada palabra o sólo el comienzo del texto.
  • paritialChars: Cantidad mínima de carácteres necesarios para que se active la búsqueda parcial.
  • fullSearch: Tomar en cuenta cualquier subcadena de las opciones o sólo los comienzos (palabra o línea).
  • ignoreCase: Tomar o no en cuenta la diferencia entre mayúsculas/minúsculas.

Enlaces:

La típica distribución de una página web

Distribución típica de una página web
Distribución típica de una página web

Esta es la distribución típica de una página web.  Esta compuesta por los siguientes elementos.

  • Cabecera.
  • Columna izquierda.
  • Contenido central.
  • Columna derecha.
  • Pies.

De acuerdo con los estándares actuales, es preferible utilizar divs en lugar de tables (donde sea humanamente posible) ya que el documento HTML debería encargarse de definir el contenido mientras que a través de la definición de estilos (CSS) se debería establecer la presentación, es decir, como se ordena y distribuye el contenido a lo largo y ancho de la página web.

Como lo he mencionado en otras ocasiones para mi CSS es todavía una ciencia oculta, hay muchas cosas que frecuentemente se me enrredan cuando utilizo CSS.  Sin embargo ultimamente lo he estado utilizando un poco mas y he adquirido mas práctica y cada vez me siento mas a gusto con utilizándolo.

Ahora implementar este diseño es algo sencillo para mi … bueno, casi.

Parto de la siguiente estructura de contenido.

        <div id='pagina'>
            <div id='cabecera'>
                Cabecera <br />
            </div>

            <div id='colizq'>
                Columna Izquierda <br />
            </div>

            <div id='contenido'>
                Contenido <br />
            </div>

            <div id='colder'>
                Columna Derecha <br />
            </div>

            <div id='pies'>
                Pies
            </div>
        </div>

El resultado inicial es claramente inaceptable.

Resultado #1
Resultado #1

Agregamos un poco de estilo para enrriquecer a la presentación.

    #pagina
    {
        width: 1024px;
        margin: auto;
        background-color: pink;
    }

Con esto le damos un ancho suficiente para visualizar correctamente el contenido del sitio y lo centramos para que se vea bonito en monitores mas anchos.

    #cabecera
    {
        padding: 5px;
        background-color: orange;
    }

    #colizq
    {
        padding: 5px;
        background-color: yellow;
        width: 300px;
    }

    #contenido
    {
        padding: 5px;
        background-color: blue;
        width: 394px;
    }

    #colder
    {
        padding: 5px;
        background-color: red;
        width: 300px;
    }

    #pies
    {
        padding: 5px;
        background-color: green;
    }

Les damos colores diferentes a cada una de las partes para diferenciarlas, un padding para separar un poco el borde del contenido de cada uno de los divs y en el caso de la parte central, el ancho específico que se requiera.

Resultado #2
Resultado #2

Ya tiene cara de algo, pero no necesariamente lo que estamos buscando.  Es necesario ordenar las columnas izquirda, centro y derecha para que se ubiquen de la forma como sus mismos nombres lo indican.

En CSS esto equivale a decirle a los divs que floten (float) en la dirección que queramos.

    #colizq
    {
        float: left;
        padding: 5px;
        background-color: yellow;
        width: 300px;
    }

    #contenido
    {
        float: left;
        padding: 5px;
        background-color: blue;
        width: 394px;
    }

    #colder
    {
        float: left;
        padding: 5px;
        background-color: red;
        width: 300px;
    }

El atributo adicionado les indica a los divs que vayan flotando y arreglándose a la izquierda cada vez que son agregados a la presentación.

Resultado #3
Resultado #3

En algunos casos, cuando continúan mas divs y necesitamos que se alineen en una la siguiente fila, es necesario indicarle a un div específico que no permita que otros divs floten a uno/ninguno de sus lados.  Para esto se utiliza el atributo clear (left, right, none, both).

Para el caso de la sección de los pies se complementaría de la siguiente manera.

    #pies
    {
        padding: 5px;
        background-color: green;
        clear: left;
    }

Hasta ahí todo es muy fácil: un pequeño paso para el lector, pero hace un año fue un gran paso para mi.  Pero aún me queda una duda por solucionar.

Qué pasa cuando las columnas -patriotas- tienen altos de diferente longitud ?

Resultado #4.
Resultado #4.

Se nota ese sobrante rosado bajo las columnas izquierda y derecha ?

Si revisamos las clases CSS, la rosada es la #pagina, esto significa que #colizq y #colder no están ocupando el mismo espacio vertical que #contenido hasta #pies y está dejando ver la capa inmediatamente inferior.

Para los “diseños” que comúnmente utilizo esto no es un problema ya que me gustan los diseños minimalistas y el blanco es mi fondo preferido.  Pero que va a pasar el día en que necesite que una columna sea de un fondo diferente del resto ?

Mi pregunta es: cómo hago para que las columnas ocupen la totalidad del espacio vertical disponible entre #cabecera y #pies ?  La duda persiste.

Enlace: código de demostración.

Primer ejemplo de XML-RPC

Estimando que probablemente uno de los proyectos de la Fundación requiera la consulta de información de uno de los proyectos anteriores ubicado en uno de los servicios de hosting, decidí realizar algunos experimientos con XML-RPC para determinar la viabilidad de utilizarlo para esta tarea en caso de que sea realmente necesaria. Debido a que el hosting en el que se encuentra el proyecto tiene una versión bastante antigua de PHP4 se decidió utilizar la librería de http://phpxmlrpc.sourceforge.net/.

Este es mi primer contacto con XML-RPC. Había leido un poco del tema en la documentación del framework CodeIgniter que planeo utilizar dentro de poco en mis próximos proyectos, sin embargo como está basado en PHP5 y el hosting soporta únicamente PHP4 decidí probar con la librería mencionada anteriormente, la cual es bastante mas enrredada, pero por suerte bien documentada, aunque hacen falta ejemplos.

En este pequeño demo que implementé se envían por parte del cliente un nombre (“Pepe”) y la solicitud de ejecución de un procedimiento remoto (“HelloWorld.show”). El servidor relaciona este método remoto con un procedimiento local (“show”) que retorna la cadena resultante (“Hello Pepe”).

El Cliente.

Se realiza el include de la librería de XML-RPC.

include_once (“../lib/xmlrpc-2.2/lib/xmlrpc.inc”);

Se define la ubicación del servidor y se instancia un objeto de la clase cliente.

$host = “localhost”;
$location = “xmlrpc/d1”;
$server = “ServerHello.php”;

$client = new xmlrpc_client(“http://{$host}/{$location}/{$server}”);

Se prepara el mensaje que enviará el cliente. El mensaje define cual es el procedimiento remoto a ejecutarse y la información relacionada con este (parámetros). Para el ejemplo se invocará al procedimiento HelloWorld.show y se le enviará “Pepe” que es de tipo string.

$message = new xmlrpcmsg(“HelloWorld.show”, array(new xmlrpcval(“Pepe”, “string”)));

Si se desea consultar los mensajes de depuración se debe activar la siguiente expresión.

// $client -> setDebug(3);

Con todo listo, se envía la solicitud al servidor y se captura su respuesta.

$response = $client -> send($message);

Si la respuesta referencia a un error, este viene con su respectivo código de fallo, en caso contrario se puede consultar su contenido.

if ($response -> faultCode())
echo “<br>KO. Error: ” . $response -> faultString();
else
echo “<br>OK. got … ” . $response -> value() -> scalarVal();

El Servidor.

Se realiza el include de la librería de XML-RPC, se incluyen los dos archivos inc: cliente y servidor.

include_once (“../lib/xmlrpc-2.2/lib/xmlrpc.inc”);
include_once (“../lib/xmlrpc-2.2/lib/xmlrpcs.inc”);

Se instancia un objeto de clase servidor en la que se especifica la siguiente información de los procedimientos remotos expuestos como disponibles:

  • Relación entre nombre de procedimiento remoto y función PHP a ejecutarse en su llamado.
  • La firma de la función (signature): tipo de retorno y de parámetros requeridos. Una función puede tener varias firmas.
  • Documentación de la función (docstring).

$server = new xmlrpc_server(array(
“HelloWorld.show” => array(“function” => “show”,
“signature” => array(array($xmlrpcValue, $xmlrpcString)),
“docstring” => “Shows a greeting message with the specified name.”)
), false);

Si se desea consultar los mensajes de depuración se debe activar la siguiente expresión.

// $server -> SetDebug(3);

Cuando todo está listo se envía la respuesta al cliente.

$server -> service();

Como siguiente paso se debe definir la función PHP show que será ejecutada cuando el procedimiento remoto HelloWorld.show sea solicitado.

La función recibe la información enviada por el cliente ($xmlrpcmsg) y tiene acceso a la variable $xmlrpcerruser la cual indica el número del error a partir del cual se encuentran disponibles para los errores definidos por el usuario.

function show($xmlrpcmsg)
{
global $xmlrpcerruser;

Verifico que el número de parámetros enviados por el cliente sea de uno, en caso contrario envío un código de error.

if ($xmlrpcmsg -> getNumParams() != 1)
{
return new xmlrpcresp(0, $xmlrpcerruser+1, “I am expecting a parameter for the name!”);
}

Obtengo el valor del parámetro disponible.

$val0 = $xmlrpcmsg -> getParam(0);

if($val0 -> kindOf() != “scalar”)
{
return new xmlrpcresp(0, $xmlrpcerruser+2, “I am expecting a string as parameter for the name!”);
}

$name = $val0 -> scalarval();

Compongo la cadena resultante: “Hello ” + valor del parámetro y la empaqueto en un mensaje de respuesta (xmlrpcresp) el cual es de tipo string y es enviado hacia el cliente.

return new xmlrpcresp(new xmlrpcval(“Hello ” . $name, “string”));
}

El demo #1 en ejecución puede ser consultado en http://demo.jorgeivanmeza.com/PHP/XMLRPCDemo/0.1/ClientHello.php

La documentación en línea de XML-RPC for PHP puede ser consultada en http://phpxmlrpc.sourceforge.net/doc-2/.