Selecciones AJAX encadenadas

En el tutorial de hoy, crearemos un conjunto de elementos de selección encadenados. Al seleccionar una opción en uno de ellos, se activará una actualización en la página, mostrándole más opciones para refinar su selección. Describiremos las opciones del lado del servidor con PHP, por lo que le resultará fácil conectar el ejemplo de hoy a una base de datos.

La idea de este tutorial fue sugerida por Casper Hansen de Dinamarca.

El HTML

Como puede ver en la captura de pantalla a continuación, el cuadro de selección va acompañado de un título que explica de qué se trata la selección. El título y el cuadro de selección se incluyen en un elemento LI.

Al agregar más preguntas, jQuery crea LI adicionales. Todos estos se encuentran dentro de una lista desordenada llamada #questions . El título y las opciones para estos elementos se sirven como JSON, como verá en la parte de PHP del tutorial. Este es el marcado que se genera para los elementos li:

index.html - código generado

<ul id="questions">
    <!-- Generated by jQuery -->
    <li>
        <p>What would you like to purchase?</p>
        <select data-placeholder="Choose a product category">
            <option data-connection="phoneSelect" value="Phones">Phones</option>
            <option data-connection="notebookSelect" value="Notebooks">Notebooks</option>
            <option data-connection="tabletSelect" value="Tablets">Tablets</option>
        </select>
    </li>
    <!-- The next sections are inserted here depending on the choices above -->
</ul>

Es posible que observe en la página de demostración que no estamos utilizando los controles de selección predeterminados del navegador. Esto se debe a que estamos utilizando el complemento Chosen jQuery para actualizar nuestras selecciones en los elegantes widgets que ve. Simplemente necesitamos llamar al chosen() en las selecciones, y el complemento se encargará del resto.

El código jQuery

En resumen, esto es lo que hace nuestro código jQuery:obtiene la información de los cuadros de selección como JSON del servidor, genera su HTML y configura detectores de eventos para los cambios de selección. Si se produce un cambio en la selección, el proceso se repite para el nuevo elemento seleccionado.

En el código, esto se logra mediante dos funciones de JavaScript:

  • actualizar selecciones activa el complemento Elegido y vincula los detectores de eventos cada vez que se agrega un elemento a la página;
  • buscarSeleccionar solicita un feed JSON del servidor y genera el marcado a partir de la respuesta.

Puedes verlos a continuación.

activos/js/script.js

$(function(){

    var questions = $('#questions');

    function refreshSelects(){
        var selects = questions.find('select');

        // Improve the selects with the Chose plugin
        selects.chosen();

        // Listen for changes
        selects.unbind('change').bind('change',function(){

            // The selected option
            var selected = $(this).find('option').eq(this.selectedIndex);
            // Look up the data-connection attribute
            var connection = selected.data('connection');

            // Removing the li containers that follow (if any)
            selected.closest('#questions li').nextAll().remove();

            if(connection){
                fetchSelect(connection);
            }

        });
    }

    var working = false;

    function fetchSelect(val){

        if(working){
            return false;
        }
        working = true;

        $.getJSON('ajax.php',{key:val},function(r){

            var connection, options = '';

            $.each(r.items,function(k,v){
                connection = '';
                if(v){
                    connection = 'data-connection="'+v+'"';
                }

                options+= '<option value="'+k+'" '+connection+'>'+k+'</option>';
            });

            if(r.defaultText){

                // The chose plugin requires that we add an empty option
                // element if we want to display a "Please choose" text

                options = '<option></option>'+options;
            }

            // Building the markup for the select section

            $('<li>\
                <p>'+r.title+'</p>\
                <select data-placeholder="'+r.defaultText+'">\
                    '+ options +'\
                </select>\
                <span class="divider"></span>\
            </li>').appendTo(questions);

            refreshSelects();

            working = false;
        });

    }

    $('#preloader').ajaxStart(function(){
        $(this).show();
    }).ajaxStop(function(){
        $(this).hide();
    });

    // Initially load the product select
    fetchSelect('productSelect');
});

¡Excelente! Ahora nos queda generar el feed JSON real. Observe que fetchSelect La función toma un argumento de cadena. Esta es la clave que le devolveremos a PHP, indicando qué conjunto de elementos queremos.

Aquí hay una respuesta de muestra de nuestro script PHP:

{
    "items": {
        "Phones": "phoneSelect",
        "Notebooks": "notebookSelect",
        "Tablets": ""
    },
    "title": "What would you like to purchase?",
    "defaultText": "Choose a product category"
}

buscarSeleccionar recorre los elementos y utiliza las claves como contenido de los elementos de opción y los valores como conexiones. Los teléfonos y las computadoras portátiles harían que la secuencia de comandos generara nuevos cuadros de selección, mientras que las tabletas no lo harían.

El PHP

Necesitamos almacenar de alguna manera la información sobre los cuadros de selección, las opciones que contienen y las conexiones entre ellos. Con una base de datos, esto podría hacerse seleccionando un conjunto específico de filas. Pero aquí almacenaremos estos datos estáticamente como objetos. Para este propósito, definiremos una clase simple que contendrá la información para un cuadro de selección:

ajax.php/1

// Each select box will be an instance of this class

class SelectBox{
    public $items = array();
    public $defaultText = '';
    public $title = '';

    public function __construct($title, $default){
        $this->defaultText = $default;
        $this->title = $title;
    }

    public function addItem($name, $connection = NULL){
        $this->items[$name] = $connection;
        return $this;
    }

    public function toJSON(){
        return json_encode($this);
    }
}

Ahora solo necesitamos crear una instancia de esta clase para cada cuadro de selección y llamar a addItem() para agregar opciones. Este método tiene un parámetro de conexión de $ opcional, que contiene el nombre de un cuadro de selección dependiente.

ajax.php/2

/* Configuring the selectboxes */

// Product selectbox

$productSelect = new SelectBox('What would you like to purchase?','Choose a product category');
$productSelect->addItem('Phones','phoneSelect')
              ->addItem('Notebooks','notebookSelect')
              ->addItem('Tablets','tabletSelect');

// Phone types

$phoneSelect = new SelectBox('What kind of phone are you interested in?', 'Pick a phone type');
$phoneSelect->addItem('Smartphones','smartphoneSelect')
            ->addItem('Feature phones','featurephoneSelect');

// Smartphones

$smartphoneSelect = new SelectBox('Which is your desired smartphone?','Choose a smartphone model');
$smartphoneSelect->addItem('Samsung Galaxy Nexus')
                 ->addItem('iPhone 4S','iphoneSelect')
                 ->addItem('Samsung Galaxy S2')
                 ->addItem('HTC Sensation');

// Feature phones

$featurephoneSelect = new SelectBox('Which is your desired featurephone?','Choose a feature phone');
$featurephoneSelect->addItem('Nokia N34')
                   ->addItem('Sony Ericsson 334')
                   ->addItem('Motorola');

// iPhone colors

$iphoneSelect = new SelectBox('What color would you like?','Choose a color');
$iphoneSelect->addItem('White')->addItem('Black');

// Notebook select

$notebookSelect = new SelectBox('Which notebook would you like to buy?', 'Choose a notebook model');
$notebookSelect->addItem('Asus Zenbook','caseSelect')
               ->addItem('Macbook Air','caseSelect')
               ->addItem('Acer Aspire','caseSelect')
               ->addItem('Lenovo Thinkpad','caseSelect')
               ->addItem('Dell Inspiron','caseSelect');

// Tablet select

$tabletSelect = new SelectBox('Which tablet would you like to buy?', 'Pick a tablet');
$tabletSelect->addItem('Asus Transformer','caseSelect')
             ->addItem('Samsung Galaxy Tab','caseSelect')
             ->addItem('iPad 16GB','caseSelect')
             ->addItem('iPad 32GB','caseSelect')
             ->addItem('Acer Iconia Tab','caseSelect');

// Case select

$caseSelect = new SelectBox('Buy protective casing?','');
$caseSelect->addItem('Yes')->addItem('No');

// Register all the select items in an array

$selects = array(
    'productSelect'         => $productSelect,
    'phoneSelect'           => $phoneSelect,
    'smartphoneSelect'      => $smartphoneSelect,
    'featurephoneSelect'    => $featurephoneSelect,
    'iphoneSelect'          => $iphoneSelect,
    'notebookSelect'        => $notebookSelect,
    'tabletSelect'          => $tabletSelect,
    'caseSelect'            => $caseSelect
);

El código anterior define una serie de elementos seleccionados y los coloca en $selects formación. Cuando este script recibe una solicitud AJAX, buscará en esta matriz y devolverá una respuesta:

ajax.php/3

// We look up this array and return a select object depending
// on the $_GET['key'] parameter passed by jQuery

// You can modify it to select results from a database instead

if(array_key_exists($_GET['key'],$selects)){
    header('Content-type: application/json');
    echo $selects[$_GET['key']]->toJSON();
}
else{
    header("HTTP/1.0 404 Not Found");
    header('Status: 404 Not Found');
}

Llamando al toJSON() método que definimos al principio, generamos todos los datos para el objeto seleccionado como JSON, listos para usar en nuestra interfaz de jQuery.

¡Con esto, nuestro ejemplo de Selecciones AJAX encadenadas está completo!

Terminado

Puede usar este ejemplo para impulsar guías de usuario, recomendaciones de productos o páginas de búsqueda. Actualizar la secuencia de comandos para usar una base de datos en vivo es sencillo y en realidad simplificará la secuencia de comandos PHP.