HTML 5 Datalist: See you Datapicker, Autocomplete, etc.

Recuerdo hace unos 6 años cuando se puso de moda el tener campos de textos en los formularios y búsquedas con la opción de auto completado o datapicker. Sin embargo, para los desarrolladores era bastante tedioso el tener que montar los resultados a base de un segundo control (div o ul) y estilos para que encajara justo debajo del input, los posibles resultados según lo que el usuario escribiera dentro de ese campo. Un ejemplo claro de este tipo de componentes a medida es Autocomplete de JQuery UI. Si echamos un vistazo al código fuente del ejemplo, vemos que la forma de mostrar la información es haciendo uso de un ul y aplicándole los estilos necesarios para que sea relativo al input… En fin, nada con lo que no hayamos tenido que lidiar más de uno.

En la especificación de HTML 5 ya disponemos del elemento Datalist, el cual va aportarnos esta funcionalidad de una forma muchísimo más sencilla (Ni estilos, ni divs, ni ul).

Datalist

Se trata de un elemento que contiene posibles valores que podemos asignar a un input del tipo text relacionado con él a través de la propiedad list

        <input type="text" id="txtCity" list="cities" placeholder="Type a city" />
        <datalist id="cities">
            <option value="Madrid"></option>
            <option value="Barcelona"></option>
            <option value="Sevilla"></option>
            <option value="Roma"></option>
            <option value="New York"></option>
            <option value="London"></option>
            <option value="Manhattan"></option>
        </datalist>

Como podemos ver en el código anterior, el input txtCity tiene un nuevo atributo llamado list, el cual contiene el id del datalist asociado. Dicho elemento contendrá a través de elementos options posibles valores para ese campo de texto. Lo mejor, es que el comportamiento de este control es el que esperamos de uno de tipo autocomplete o datapicker, donde van apareciendo sugerencias según vamos escribiendo.

Datalist & AJAX

Otro de los escenarios más comunes en el uso de campos con autocomplete es que se realice la búsqueda en servidor y cargue dinámicamente los valores disponibles en el Datalist. En este caso he creado el siguiente ejemplo en ASP.NET Web Api:

using System.Linq;
using System.Web.Http;

namespace DataList.Controllers
{
    public class ValuesController : ApiController
    {
        private readonly string[] _cities =
        {
            "Madrid",
            "Barcelona",
            "Sevilla",
            "Roma",
            "New York",
            "London",
            "Manhattan",
            "Shanghai",
            "Istanbul",
            "Karachi",
            "Mumbai",
            "Moscow",
            "São Paulo",
            "Tokyo",
            "Mexico City",
            "Tehran",
            "Lima",
            "Cairo",
            "Rio de Janeiro",
            "Singapore",
            "Los Angeles",
            "Berlin"
        };

        // GET api/values/5
        public string[] Get(string val)
        {
            return (from c in _cities
                    where c.Contains(val)
                    select c).ToArray();
        }
    }
}

El ejemplo es bastante simple, ni siquiera consulta a base de datos ;), tenemos implementado el HTTP verb Get, el cual recibe el parámetro val que corresponde con la consulta que está haciendo el usuario. Cuando llega un valor, la Api devolverá todos aquellas ciudades que contengan ese conjunto de caracteres. Desde el lado del cliente, podríamos resolverlo de la siguiente manera:

        <input type="text" id="txtCityAJAX" list="citiesAJAX" placeholder="Type a city" />
        <datalist id="citiesAJAX"></datalist>

Por un lado, creamos los elementos input y datalist relacionados entre sí por la propiedad list. En este caso, el datalist no tienen ningún valor precargado ya que haremos la magia oportuna desde el lado de JavaScript:

    <script src="~/Scripts/jquery-2.0.3.min.js"></script>
    <script src="~/Scripts/underscore.min.js"></script>
    <script>
        $(function () {

            function searchCities() {
                var val = $(this).val();
                if (val == "") return;

                $.ajax({
                    url: '/api/values',
                    method: 'GET',
                    data: { val: val },
                    contentType: 'application/json'
                }).then(function (data) {

                    var $citiesAJAX = $("#citiesAJAX");
                    $citiesAJAX.empty();

                    for (var city in data) {
                        $citiesAJAX.append("<option value='" + data[city] + "'></option>");
                    }
                });
            }

            var searchCitiesDebounce = _.debounce(searchCities, 100);

            $("#txtCityAJAX").on("input", searchCitiesDebounce);
        });
    </script>

Para este ejemplo he utilidado JQuery y Underscorejs. Básicamente lo que hago es, en primer lugar, crearme una función llamada searchCities donde recupero el valor del campo de texto, compruebo que sea distinto de vacío, y realizo una petición Ajax a mi Api. Cuando la llamada me retorna el array con los valores que encajan en mi búsqueda, recupero el elemento datalist y añado cada uno de los valores que me ha devuelto mi consulta.
Como no quiero que cada vez que el usuario presione una tecla vaya directamente al servidor, hago uso del método _.debounce de Underscorejs, el cual me permite marcar un tiempo de espera en la ejecución de mi llamada a searchCities, con el objetivo de que al usuario le dé tiempo a escribir unas cuantas letras antes de hacer la llamada.

Espero que haya sido de utilidad 🙂

TGIF!

¡Saludos!