7 de marzo de 2010

Autocomplete JQuery UI en ASP.Net (Autocompletar)

jQueryUI_thumb1

Veremos un ejemplo de como implementar jQuery UI Autocomplete en ASP.Net o también conocido como Autocompletar de jQueryUI. Podrás además descargarte el código fuente.

Hoy hablaremos de la colección de componentes jQuery UI, y en particular del control Autocomplete. En esta biblioteca de componentes basada en jQuery podremos encontrar interesantísimos controles y efectos visuales para la creación de modernas aplicaciones web.

jQuery UI Autocomplete

Con este artículo pretendo que veamos el código completo asociado a 2 ejemplos prácticos:

jQueryUI Autocomplete Demo

1. jQueryUI Autocomplete Ajax Básico: Este ejemplo es el uso sencillo del Autocompletar jQuery UI, con una configuración muy básica, y obteniendo los datos a partir de una llamada ASP.Net Ajax.

2. jQueryUI Autocomplete Ajax con Cache: Este otro ejemplo de Autocompletar jQuery UI lo completamos un poco más, además de la llamada Ajax, implementamos el uso de una cache para reducir las llamadas al Servidor.

 

 

jQueryUI Autocomplete ASP.Net Ajax Básico

Pero vallamos a la práctica. Lo primero que haremos será escribir el código para poner un simple input en una pagina web, y cuando escribamos, al menos 2 caracteres en el input, consultaremos un WebMethod (C#) que nos devolverá en formato JSON, la lista de las ciudades que coincidan con los caracteres tecleados.

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="jQueryUIAutocomplete.aspx.cs"     Inherits="jQueryUI_jQueryUIAutocomplete" %>
 
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
  <title>jQueryUI Autocomplete en ASP.Net (Simple)</title>
 
  <link href="css/redmond/jquery-ui-1.8rc3.custom.css" rel="stylesheet" type="text/css" />
  <script type="text/javascript"  
          src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.js"></script>
  <script type="text/javascript" src="js/jquery-ui-1.8rc3.custom.min.js"></script>
 
  <script type="text/javascript">
    jQuery(document).ready(function () {
      $("#txtCiudad").autocomplete({
        source: function (request, response) {
          PageMethods.ObtCiudades(request.term,
            function (data) {
              var ciudades = (typeof data) == 'string' ? eval('(' + data + ')') : data;
              response(ciudades);
            },
            fnLlamadaError);
        },
        minLength: 2
      });
    });
 
   function fnLlamadaError(excepcion) {
      alert('Ha ocurrido un error interno: ' + excepcion.get_message()); 
    }
  </script>
 
  <style type="text/css">
         body{ font: 62.5% "Trebuchet MS", sans-serif; margin: 50px;}
    input { width:200px; }
  </style>
</head>
<body>
  <form id="form1" runat="server">
    <asp:ScriptManager EnablePageMethods="true" ID="ScriptManager1" runat="server">
    </asp:ScriptManager>
    <div class="ui-widget">
       <label for="txtCiudad" style='padding-left:20px;'>Ciudad: </label>
       <input id="txtCiudad" />
    </div>
  </form>
</body>
</html>
 
 

El resultado sería el siguiente:

jQueryUI Autocomplete ASP.NetNótese que al escribir 2 caracteres (ma) filtra los resultados mostrando todas aquellas ciudades que contienen dicha cadena.

Pero comentemos un poco el código:

1. Inicialmente hacemos las referencias respectivas al css, y a las 2 bibliotecas (jQuery.js y jquery-ui-1.8rc3.custom.min.js)

2. Lo proximo que hacemos es asociar el input txtCiudad con el widget autocomplete de jQuery UI; aquí debemos comentar algunas opciones o propiedades que definimos, pero eso lo haré más adelante.

3. Posteriormente definimos la funcion “fnLlamadaError” que será ejecutada si ocurre algun error en la llamada Ajax al servicio web.

4. Dentro del <body>, incluimos el ScriptManager, necesario para poder hacer la llamada AJAX del PageMethod, podriamos haber usado la funcion Ajax de jQuery y en ese caso no sería necesario el control ScriptManager, pero me parece más simple el uso de los PageMethods.

5. Y por último, incluimos los controles HTML necesarios, en particular el control inputtxtCiudad”.

 

Detallando las opciones del AutoComplete

$("#txtCiudad").autocomplete({ 
    source: function (request, response)
    {
      PageMethods.ObtCiudades(request.term,
          function (data)
          {
             var ciudades = (typeof data) == 'string' ? eval('(' + data + ')') : data;
             response(ciudades);
          },
          fnLlamadaError); 
    },
    minLength: 2
}); 

En este código es donde asociamos el control “txtCiudad” con el widget Autocomplete de jQuery UI. Para el cual estamos definiendo 2 propiedades:

  1. Definimos la propiedad source del autocomplete asociandole una función, dentro de dicha función lo que hacemos es una llamada Ajax a nuestro servicioPageMethods.ObtCiudades” (en este caso es un PageMethods, pero podría haber sido una llamada a un web services o un WCF). La llamada al PageMethods tiene 3 parámetros, el primero es el termino o cadena introducida en el input, el segundo parámetro es la función que ejecutaremos si todo marcha bien, y el tercer parámetro es la función que se ejecuta si algo marcha mal.
  2. El segundo parámetro minLength lo establecemos a 2, indicándole al Widget Autocompletar de jQueryUI, que debe lanzar la búsqueda después de escribir al menos 2 caracteres en el input.

PageMethods o Servicio ObtCiudades

[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public static List<AutoCompleteResult> ObtCiudades(string term)
{
  var lstCiudades = new List<Ciudad>()
      {
        new Ciudad { Iata="MAD", Nombre = "Madrid", Pais = "España"},
        new Ciudad { Iata="BCN", Nombre = "Barcelona", Pais = "España" },
        new Ciudad { Iata="AGP", Nombre = "Malaga", Pais = "España" },
        new Ciudad { Iata="LON", Nombre = "Londres", Pais = "Inglaterra" },
        new Ciudad { Iata="HAV", Nombre = "Habana", Pais = "Cuba" },
        new Ciudad { Iata="MDR", Nombre = "Madeira", Pais = "Mexico"},
        new Ciudad { Iata="MDI", Nombre = "Madinson", Pais = "Estados Unidos" },
        new Ciudad { Iata="MGS", Nombre = "Madagascar", Pais = "Madagascar" },
        new Ciudad { Iata="DLN", Nombre = "Madelaine", Pais = "Canada" },
        new Ciudad { Iata="MRA", Nombre = "Madurai", Pais = "India" }
      };
 
  term = term.ToLower();
  var ciudades = from c in lstCiudades
                 where c.Iata == term || c.Nombre.ToLower().Contains(term)
                 select new AutoCompleteResult
                        { id = c.Iata, value = c.Nombre + ", " + c.Pais };
 
  return ciudades.ToList();
}

Nota: En nuestro ejemplo hemos simulado la existencia de una lista de ciudades, en un caso real probablemente tengamos que consultar una base de datos. Además usamos en este ejemplo 2 clases que no definimos aquí  porque no es el objetivo, pero puedes fácilmente intuir su estructura.

 

jQueryUI Autocomplete Ajax con Cache

Este segundo caso (jQuery UI Autocomplete) lo complicaremos un poco más, pero solo un poco, en realidad lo nuevo sería lo siguiente:

  1. Crearemos una especie de cache. Así si ya hemos realizado una búsqueda con un termino o cadena determinada, sí intentamos realizar la misma búsqueda pues no será necesario ir nuevamente a efectuar la consulta Ajax al servidor, pues ya tendremos el resultado en nuestra cache.
  2. Además agregaremos otro control input de Autocompletar jQuery UI.
  3. Agregaremos también un control de resultados (log) donde cada ves que escojamos una ciudad, se guardará o visualizará la ciudad seleccionada.

Pero como “vista hace fe…” veamos una imagen de la página web que haremos:

jQueryUI Autocomplete ASP.Net Cache Es de suponer que los 2 input de las ciudades implementen el autocompletar de jQuery UI, y ambos controles compartirán la misma cache; pero veamos el código fuente de la página web:

 

Autocomplete jQuery UI con Cache, Código aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="jQueryUIAutocompleteCache.aspx.cs" Inherits="jQueryUI_jQueryUIAutocomplete" %>
 
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
  <title>jQueryUI Autocomplete en ASP.Net con Cache</title>
 
  <link href="css/redmond/jquery-ui-1.8rc3.custom.css" rel="stylesheet" type="text/css" />
  <script type="text/javascript"
     src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.js"></script>
  <script type="text/javascript" src="js/jquery-ui-1.8rc3.custom.min.js"></script>
 
       <script type="text/javascript">
         jQuery(document).ready(function () {
 
           $("#txtOrigen").autocomplete({
             source: function (request, response) {
 
               //Verificamos si el termino de búsqueda ya esta en la cache
               var ciudades = $("body").data(request.term);
               if (ciudades != undefined && ciudades != null) {
                 response(ciudades);
               }
               else {
                 PageMethods.ObtCiudades(
                                        request.term,
                                        function (data) {
                                          ciudades = (typeof data) == 'string' ?
                                                      eval('(' + data + ')') : data;
 
                                          //Guardamos en la cache el resultado.
                                          $("body").data(request.term, ciudades);
 
                                          response(ciudades);
                                        },
                                        fnLlamadaError);
               }
             },
             minLength: 2,
             select: function (event, ui) {
               //Mostramos lo seleccionado en el div resultados
               log(ui.item ? ("Origen Seleccionado: " + ui.item.value + ", IataCod: "
                   ui.item.id) : "Ninguna Selección con el termino: " + this.value);
             }
           });
 
           $("#txtDestino").autocomplete({
             source: function (request, response) {
 
               //Verificamos si el termino de búsqueda ya esta en la cache
               var ciudades = $("body").data(request.term);
               if (ciudades != undefined && ciudades != null) {
                 response(ciudades);
               }
               else {
                 PageMethods.ObtCiudades(
                                        request.term,
                                        function (data) {
                                          ciudades = (typeof data) == 'string' ?
                                                      eval('(' + data + ')') : data;
 
                                          //Guardamos en la cache el resultado.
                                          $("body").data(request.term, ciudades);
 
                                          response(ciudades);
                                        },
                                        fnLlamadaError);
               }
             },
             minLength: 2,
             select: function (event, ui) {
               //Mostramos lo seleccionado en el div resultados
               log(ui.item ? ("Destino Seleccionado: " + ui.item.value + ", IataCod: " +
                   ui.item.id) : "Ninguna Selección con el termino: " + this.value);
             }
           });
 
         });
 
         function fnLlamadaError(excepcion) {
           alert('Ha ocurrido un error interno: ' + excepcion.get_message());
         }
 
         function log(message) {
           $("<div/>").text(message).prependTo("#log");
         }
 
       </script>
 
  <style type="text/css">
         body{ font: 62.5% "Trebuchet MS", sans-serif; margin: 50px;}
    input { width:200px; }
  </style>
</head>
<body>
  <form id="form1" runat="server">
    <asp:ScriptManager EnablePageMethods="true" ID="ScriptManager1" runat="server"></asp:ScriptManager>
 
  <div>
    <div class="ui-widget">
       <label for="txtOrigen">Ciudad Origen: </label>
       <input id="txtOrigen" />
 
       <label for="txtDestino" style='padding-left:20px;'>Ciudad Destino: </label>
       <input id="txtDestino" />
    </div>
 
    <div class="ui-widget" style="margin-top:2em; font-family:Arial">
       Resultados:
       <div id="log" style="height: 200px; width: 400px; overflow: auto;" class="ui-widget-content"></div>
    </div>
  </div>
  </form>
</body>
</html>

Este código es muy similar al mostrado en el primer ejemplo (jQueryUI Autocomplete ASP.Net Ajax Básico) y también su explicación, por ende solo comentaremos las cosas nuevas que hemos agregado:

$("#txtOrigen").autocomplete({
  source: function (request, response) {
 
  //Verificamos si el termino de búsqueda ya esta en la cache
  var ciudades = $("body").data(request.term);
  if (ciudades != undefined && ciudades != null) {
    response(ciudades);
  }
  else {
    PageMethods.ObtCiudades(
                            request.term,
                            function (data) {
                              ciudades = (typeof data) == 'string' ?
                                          eval('(' + data + ')') : data;
 
                              //Guardamos en la cache el resultado.
                              $("body").data(request.term, ciudades);
 
                              response(ciudades);
                            },
                            fnLlamadaError);
  }
}, 
minLength: 2,
select: function (event, ui) { 
  //Mostramos lo seleccionado en el div resultados
  log(ui.item ? ("Origen Seleccionado: " + ui.item.value + ", IataCod: "
      ui.item.id) : "Ninguna Selección con el termino: " + this.value);
}

En este código es donde asociamos el control “txtOrigen” con el widget Autocomplete de jQuery UI. Para el cual estamos definiendo 3 propiedades:

  1. Definimos la propiedad source del autocomplete asociandole una función. En esta función lo primero que hacemos es verificar si ya tenemos en cache la búsqueda solicitada, si ya existe devolvemos el resultado de la cache, en caso contrario lo que hacemos es una llamada Ajax a nuestro servicioPageMethods.ObtCiudades” y guardar el resultado obtenido en cache.
  2. El segundo parámetro minLength lo establecemos a 2, indicándole al Widget Autocompletar de jQueryUI, que debe lanzar la búsqueda después de escribir al menos 2 caracteres en el input.
  3. El tercer parámetro select definiremos una funcion con el código que queremos se ejecute cada ves que seleccionemos una ciudad, en nuestro caso llamamos a la funcion log que pintará el resultado en un div que almacenará la historia de todas nuestras selecciones.

Nota: Este widget (jQuery UI Autocomplete) tiene algunas otras propiedades interesantes que te ayudan a personalizar su comportamiento, para más información visita la página oficial.

Artículos relacionados:

22 comentarios:

  1. Hola Amigo, muy bueno tu post, me ha ayudado mucho, pero quisiera preguntrate como se podria realizar un autocompletado tipo gmail o hotmail, osea ingresar una direccion de email y despues ingresar otro y asi sucesivamente asi por ejemplo

    primer@hotmail.com, segund@hotmail.com, .....etc

    si podrias ayudarme me serviría mucho, gracias. Saludos

    ResponderEliminar
  2. Hola Yayo, muchas gracias por tu valoración, me alegra que te sea útil.

    Para lo que me preguntas, una buena solución podría pasar por utilizar el jQuery autocomplete al estilo facebook, te recomiendo visites este enlace y veas si es este el automplete jquery que estas buscando:

    http://www.emposha.com/javascript/fcbkcomplete.html

    Suerte...

    ResponderEliminar
  3. Hola miago Derbis, primero agradecerte por la rápida respuesta. estuve observando el ejemplo que me mencionaste, es muy interesante y si es lo que busco, pero el gren problema es que esta hecho para php, y yo manejo C# y asp .net, y no se si ese tipo de ejemplo como lo podria utilizar para asp .net?. saludos.

    ResponderEliminar
  4. Continuando con mi pregunta, yo quiero utilizar algo parecido a tu ejemplo utilizando un WebMetod, pero por ejemplo he visto que para el usuao de este ejemplo utliza lo siguiente:

    $("element").fcbkcomplete({
    json_url: "fetched.txt",
    cache: true,
    filter_case: true,
    filter_hide: true,
    newel: true
    });

    ahora no se si en la ruta donde se coloca el "fetched.txt" se podría poner el nombre de una clase digamos donde esta el WebMtehod por ejemplo "Obtenerobjeto.cs".

    ResponderEliminar
  5. Hola denuevo, he intenado colocar el nombre de una clase y me sale error, tal parece que no funciona con archivos de asp .net

    ResponderEliminar
  6. He visto el mismo ejemplo en esta pagina:

    http://loopj.com/2009/04/25/jquery-plugin-tokenizing-autocomplete-text-entry/

    donde tambien indica poner la ruta de una archivo php, pero parece que no funciona para asp .net, o no se si en que me estoy equivocando

    ResponderEliminar
  7. Hola, en realidad no creo que funcione poniendo la clase (bueno de hecho ya me dices que no funciona). Probablemente, y digo probablemente, funcione poniendo un webmethod.

    A ver, en realidad no he probado hacer nada concreto con ese autocomplete, pero si estas usando c# y asp.net podrias intentar en este automplete poner en la rura un webmethod, por ej:

    $("element").fcbkcomplete({
    json_url: PageMethods.ObtCiudades(),
    cache: true,
    filter_case: true,
    filter_hide: true,
    newel: true
    })

    Algo así, aunq te repito es lo que intuyo, no estoy seguro, si tengo un chance el fin de semana lo pruebo. Debes tener en cuenta que el pagemethod tendría que devolver un string con formato JSON.

    ResponderEliminar
  8. Hola amigo Derbis deuevo por aqui molestandote :-), hice lo que me dijiste, pero la verdad me sale un error y es justamente es donde se quiere capturar la ruta, y pues como comprenderás no me captura nada. Bueno seguire haciendo algunos intentos haber que resulta. Saludos

    ResponderEliminar
  9. Hola Amigo Derbis, te comento que desistí por razones de tiempo de realizar el autocompletado con jquery. Utilice el control AutocompleteExtender para realizar lo que quería, uqe era por supuesto colocoar en una caja de texto varias palabras, pero tuve que sacrificar (si se pude llamar asi) el diseño pues yo quería realizar el efecto que tenia el autocoplete de la pagina que me mencionaste en este link:

    http://www.emposha.com/javascript/fcbkcomplete.html

    Pues me gustaría poder darle el efecto de que la palabra seleccionada se muestre en la caja de texto con un sombreado y un icono de eliminar.

    Bueno espero no haberte aburrido con este largo comentario :-), pero aun así el ejemplo del autocomplete que esta en este post me es de mucha utilidad. Y muchas gracias por responder. Saludos desde Perú.

    ResponderEliminar
  10. Hola Edu, he publicado recientemente un post que implementa el autocomplete con selección multiple:

    http://www.esasp.net/2010/05/jquery-autocomplete-facebook-con-aspnet.html

    Espero te sea útil.

    De todas formas te comento que con jQueryUI autocomplete también puedes implementar la selección multiple...

    ResponderEliminar
  11. Hola que tal,
    Vaya que esta bruto este post, me ha sido de gran utilidad, pero ahora quiero ponerlo en un Control (ascx) dentro de un Master Page pero no me ha funcionado.
    Dentro del control, tengo el div y el Codigo del Script.
    La pagina aspx que muestra el control tiene los .js .
    Y el Maste Page tiene el Script Manager.
    asi como te lo muestro, no funciona, que podria ser ??

    Muchas gracias.

    Saludos ¡¡¡

    ResponderEliminar
  12. Muy bueno el post, una consulta.
    Hay alguna forma de traer una lista de palabras que tengo en la Base de Datos ya sea con o sin acentos porque me pasa que me trae todo bien, pero si alguna palabra tiene acento no me la trae ordenada y la idea es que me muestre alfabeticamente indistintamente si tiene o no acento. Espero se entienda mi idea.
    Como puedes ver en mi sitio ahora los términos estan sin acentos ya que tengo ese problema.

    ResponderEliminar
  13. Hola, no se si todavia sea tiempo de preguntar.... pero tengo una inquietud para que por favor me pudieran ayudar. Tengo problemas al traer nombres extensos y que el usuario de le BackSpace para quitar las letras del nombre traido por primera vez y nuevamente quiera buscar otro, el control se demora en responder, ya que me imagino que al llamar tantas veces al servidor este se bloquea y me entra por la funcion fnError y bloquea todos los procesos del autocomplete, inclusive hasta la pagina. Quisiera me den una luz de como solucionar ese error. Gracias

    ResponderEliminar
  14. Trate de implementar este ejemplo usando MasterPage pero obtengo un error de "Object doesn't support this property or method"

    ResponderEliminar
  15. Excelente post, mi duda es la siguiente, cuando sustituyo la lista creada manualmente por una consulta Linq sobre una base de datos (SQL), el autocompletar me devuelve “Undefined” en cada coincidencia de la de la consulta, es decir el programa no me marca error pero el Autocompletar me muestra la lista con varios “Undefined”, según las coincidencias, espero me puedas ayudar a resolver esta duda y felicidades por tu sitio

    ResponderEliminar
  16. Muy buen Post!!
    Anonimo 29 de Abril. Tuve ese mismo problema, tienes que tomar en cuenta que las propiedades id & value deben ser minusculas. La libreria al parecer parsea el JSON response como case sensitive string, y al no encontrar la propiedad establecida devuelve undefined.

    ResponderEliminar
  17. Hola amigos alguien tendria este codigo pero en VB Y NO EN C#

    se los agredeceria batante

    ResponderEliminar
  18. Estoy utilizando el ejemplo de ajax básico y de manera ocasional y aleatoria me salta la
    fnLlamadaError. Me indica "There was an error processing the request". Cómo puedo hacer que esto no ocurra? Gracias

    ResponderEliminar
  19. El ejemplo esta muy bueno. Pero tengo una inquietud como hago el mismo ejemplo llamando los datos desde un xml.

    ResponderEliminar
  20. Hola mi amigo, antes que nada muy buen post, me ha sido de mucha utilidad. He intentado implementar el autocomplete dentro de un y no he tenido éxito.
    dentro de mi formview tengo las secciones de , y .

    Espero me puedas ayudar.

    Saludos

    ResponderEliminar
  21. Hola, excelente post, pero existe un problema cuando se implementa en paginas dentro un master.page literalmente deja de funcionar.
    Sabes a que se debe y como solucionar dicho inconveniente?

    MIL gracias de antemano.

    ResponderEliminar
  22. Buenas Tardes, con ejemplo que pones como hago para dejar el id de la ciudad seleccionada en otro textbox. Gracias

    ResponderEliminar