lunes, 2 de agosto de 2010

Ajax.net: TextBox + DropDownList + Autocomplet con Text y Value

Me he encontrado con la necesidad de hacer un textbox para introducir nombre de calles y que, según fuera introduciendo letras, me fuera ofreciendo resultados de una búsqueda en la base de datos.

Lo primero que hice fue utilizar un extensor de Ajax.net Control Toolkit llamado AutoComplete. Pero se me presentó un problemón que es lo que me ha llevado a investigar y a publicar esta solución.

Las calles las busco por su nombre pero deben ser salvados por su identificador… lo cual es muy fácil en un dropDownList ya que tienes siempre el par (text, value). ¿Pero cómo lo hago con este extensor?

Pues lo primero es montar el extensor Autocomplete.

Osea por la parte del aspx tengo:

        <asp:TextBox ID="txtbVia" runat="server" Width="253px"></asp:TextBox>
<
asp:AutoCompleteExtender ID="txtbVia_AutoCompleteExtender" runat="server"
ServiceMethod="GetCompletionList"
TargetControlID="txtbVia"
UseContextKey="True"
CompletionInterval="0">
</
asp:AutoCompleteExtender>

En el codeBehind, primero debo hacer el include de la librería de servicios web y de colecciones:

using System.Collections.Generic;
using System.Web.Services;


Y el código del servicio web que me va a devolver el listado de calles. Que, aquí viene el primer “truco del almendruco”, nos permite incluir el par de texto y valor en el dropDownList a visualizar:



[System.Web.Services.WebMethodAttribute(), System.Web.Script.Services.ScriptMethodAttribute()]
public static string[] GetCompletionList(string prefixText, int count, string contextKey)
{
List<string> items = new List<string>();

foreach (Entidades.Calles calle in BL.CallesManager.GetCalleComboList(prefixText))
{
items.Add(AjaxControlToolkit.AutoCompleteExtender.CreateAutoCompleteItem(calle.nombre , calle.codvia.ToString()));
}

return items.ToArray();
}


Vamos a explicarlo un poquito más.

La firma del método es bastante rarita, pero lo más sencillo es entrar en modo split en el diseñador del fichero ascx, pulsar en las propiedades de la extensión y que el propio diseñador te haga el WebMethod pertinente. Como se ve en la firma, debemos devolver un array de strings, por eso declaro uno al principio del cuerpo del método y por eso hago un using a System.Collections.Generic.


A continuación nos traemos los datos de la base de datos. En este caso en LinQ me traigo un Iqueryable de objetos del tipo Calles el cual recorro con un foreach y… (atención aquí está el meollo del asunto) lo añado a mi lista de cadenas como un AjaxControlToolkit.AutoCompleteExtender.CreateAutoCompleteItem(tex, value)!!


¿A que mola? Pues esto lo encontré en el blog With Great Power comes Great Response.write('Ability'). Darle las gracias, que es el único en donde encontré algo legible o entendible.


Pero la cosa se complica, porque necesito poder capturar el valor del item seleccionado en el combo, y eso solamente lo puedo hacer en el lado del cliente… para lo cual añado un método javascript que me coja dicho valor y me lo cargue en un input field del tipo hidden para poder recuperarlo desde el code behind cuando pulse en el botón de guardar.


Primero añadamos un línea más al extensor para invocar al método javascript y pongamos el script.



<script type="text/javascript">
function
viaSeleccionada(source, eventArgs) {
//alert(" Key : " + eventArgs.get_text() + " Value : " + eventArgs.get_value());
document.getElementById('<%= lblIdVia.ClientID %>').innerText = eventArgs.get_value();
}
</script>

<
asp:UpdatePanel ID="UpdatePanelVia" runat="server" UpdateMode="Conditional">
<
ContentTemplate>
<
asp:TextBox ID="txtbVia" runat="server" Width="253px"></asp:TextBox>
<
asp:AutoCompleteExtender ID="txtbVia_AutoCompleteExtender" runat="server"
ServiceMethod="GetCompletionList"
TargetControlID="txtbVia"
UseContextKey="True"
CompletionInterval="0"
OnClientItemSelected="viaSeleccionada"> //ESTA LINEA ES NUEVA
</
asp:AutoCompleteExtender>
<input ID="lblIdVia" runat="server" type="hidden" />
</ContentTemplate>
</
asp:UpdatePanel>


Aquí debo señalar varias cosas:

1. La propiedad del extensor OnClietItemSelected, que se lanza en el cliente solamente.


2. La alerta que está comentada dentro del javascript que nos vale para poder  debuguear los valores que devuelve el DropDownList.


3. El input del tipo hidden pero declarado como runat=”server” que nos vale de puente entre el cliente y el servidor. Gracias a bendragon en este foro.


4. La forma de localizar este control desde Javascript con un getElementById, y el ClientID (que puede ser muy distinto del ID que escribimos nosotros) y la carga del valor a través del innerText.


Y ya tá. Tengo un textBox que, según voy escribiendo, me va devolviendo en un DropDownlist una lista de calles que incluyen las letras o palabras que he introducido y que, cuando selecciono una, me guarda el id de la calle seleccionada dentro de un campo oculto al que puedo acceder desde el code behind.


Espero que os sea tan útil como a mí.

3 comentarios:

Arconte dijo...

Fantastico me quedo muy claro, pero la verdad es que ahora estoy parado con algo similar. Tengo dos controles, un DropDownList y un TextBox. Actualmente el Autocomplete del textbox funciona estupendo, pero necesito que el nombre de las calles sea filtrado por la seleccion que haga del dropdownlist, vale decir que si selecciono una comuna/estado/region/distrito la query cambie. Tengo entendido que se hace via el contextkey, pero no se exactamente como meter el value o text del dropdownlist al contextkey.

Si tuvieras una idea, te lo agradeceria muchisimo =)

te dejo mi correo cualquier cosa, seria interesante compartir esto con alguien arconte@scene.cl (es messenger tb :D)

Juan Quijano dijo...

Recuerda que por medio de javascript almaceno el value seleccionado en un campo hidden. Hay unos cuantos ejemplos en la red para mandar esos datos de nuevo al servidor y rellenar otro combo o el mismo.

Pero eso te toca averiguarlo a tí :)

Dorian Rallón dijo...

Parce, no sabe cuanto me ha servido este post, ademas se me abrieron muchas posibilidades consultando el codigo fuente del ajaxcontroltoolkit y consumir los metodos javascripts propios de ellos, muchas gracias por la orientacion y el tiempo que dedica a orientar a los demas de manera desinteresada!.