jQuery Autocomplete y ASP.NET
subsonic (4)
Busqué en todo este sitio y en la web un buen y simple ejemplo de autocompletar usando jQuery y ASP.NET. Quería exponer los datos utilizados por autocompletar con un servicio web (y probablemente lo hagamos a continuación). Mientras tanto, tengo esto funcionando, pero parece un poco hacky ...
En mi página tengo un cuadro de texto:
<input id="txtSearch" type="text" />
Estoy usando el autocompletado de jQuery, configurado según su ejemplo:
<link rel="stylesheet" href="js/jquery.autocomplete.css" type="text/css" />
<script type="text/javascript" src="js/jquery.bgiframe.js"></script>
<script type="text/javascript" src="js/jquery.dimensions.pack.js"></script>
<script type="text/javascript" src="js/jquery.autocomplete.js"></script>
Aquí es donde comienza a volverse hacky ... Llamo a una página en lugar de a un servicio web:
<script type="text/javascript">
$(document).ready(function(){
$("#txtSearch").autocomplete(''autocompletetagdata.aspx'');
});
</script>
En la página eliminé TODO el html y simplemente tengo esto (de lo contrario, aparecen varios bits de HTML en el menú desplegable de autocompletar):
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="autocompletetagdata.aspx.cs" Inherits="autocompletetagdata" %>
Y en mi autocompletetagdata.aspx, estoy usando SubSonic para consultar, formatear y devolver datos de la base de datos (un elemento de datos por línea):
protected void Page_Load(object sender, EventArgs e)
{
// Note the query strings passed by jquery autocomplete:
//QueryString: {q=a&limit=150×tamp=1227198175320}
LookupTagCollection tags = Select.AllColumnsFrom<LookupTag>()
.Top(Request.QueryString["limit"])
.Where(LookupTag.Columns.TagDescription).Like(Request.QueryString["q"] + "%")
.OrderAsc(LookupTag.Columns.TagDescription)
.ExecuteAsCollection<LookupTagCollection>();
StringBuilder sb = new StringBuilder();
foreach (LookupTag tag in tags)
{
sb.Append(tag.TagDescription).Append("/n");
}
Response.Write(sb.ToString());
}
Si no hace una consulta LIKE, entonces devuelve todo lo que contiene una coincidencia para los caracteres que escribe; por ejemplo, escribir "a" incluirá "Ask" y "Answer", así como "March" y "Mega." Solo quería que comenzara con el partido.
De todos modos, funciona y es bastante fácil de configurar, pero ¿hay una mejor manera?
Acabo de publicar esto en otra pregunta, pero puede anular la función de análisis en el complemento de autocompletado de jQuery para que admita cualquier salida.
Ejemplo:
$("#<%= TextBox1.ClientID %>").autocomplete("/Demo/WebSvc.asmx/SuggestCustomers", {
parse: function(data) {
var parsed = [];
$(data).find("string").each(function() {
parsed[parsed.length] = {
data: [$(this).text()],
value: $(this).text(),
result: [$(this).text()]
};
});
return parsed;
},
dataType: "xml"
});
Todo lo que se espera es una matriz de cadenas en XML ... Muy fácil de hacer ... Si usa SubSonic, debería ver el RESTHandler (¡Es un GEM oculto!), Admite consultas básicas en todos sus objetos y puede devuelve JSON / XML. Aquí hay una consulta de ejemplo que lo usa ...
/Demo/services/Customers/list.xml?CustomerName=JOHN
Si cambia list.xml a list.json, cambiará los resultados a JSON. La solicitud anterior devolverá una entidad "Cliente" fuertemente tipada. Puede cambiar el parámetro para admitir LIKE, NOT LIKE, etc ... Muy potente y toda la plomería ya está lista ...
Aquí hay un video: http://subsonicproject.com/tips-and-tricks/webcast-using-subsonic-s-rest-handler/
El servicio web o un servicio WCF le brindarán la posibilidad de una mejor interfaz. Ambos también se pueden configurar para hacer la serialización de Json.
Como tomaré una clase de WCF mientras escribo (¡estoy en un descanso, de verdad!), Diseñaré el método WCF.
[OperationContract]
[WebInvoke(RequestFormat=WebMessageFormat.Json,
ResponseFormat=WebMessageFormat.Json)]
public LookupTagCollection LookupTags( int limit, string q )
{
return Select.AllColumnsFrom<LookupTag>()
.Top(limit)
.Where(LookupTag.Columns.TagDescription)
.Like(q+ "%")
.OrderAs(LookupTag.Columns.TagDescription)
.ExecuteAsCollection<LookupTagCollection>();
}
LookupTagCollection necesita ser serializable.
Jquery 1.8 Autocompletar utiliza "término" no "q" como parámetro de la cadena de consulta. esta es la versión corta y dulce que implementé. Espero que esto ayude a alguien.
Javascript:
$(function () {
$("#autocomplete").autocomplete({
source: "/pathtohandler/handler.ashx",
minLength: 1,
select: function (event, ui) {
$(this).val(ui.item.value);
}
});
});
Manipulador ASHX:
public class SearchHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
var term = context.Request.QueryString["term"].ToString();
context.Response.Clear();
context.Response.ContentType = "application/json";
var search = //TODO implement select logic based on the term above
JavaScriptSerializer jsSerializer = new JavaScriptSerializer();
string json = jsSerializer.Serialize(search);
context.Response.Write(json);
context.Response.End();
}
public bool IsReusable
{
get
{
return false;
}
}
}
Recientemente implementé autocompletar, y se ve bastante similar. Estoy usando un ashx (Generic Handler) en lugar del aspx, pero básicamente es el mismo código en el código.
Usando el ashx, se verá algo como esto:
<script type="text/javascript">
$(document).ready(function(){
$("#txtSearch").autocomplete(''autocompletetagdata.ashx'');
});
</script>
[WebService(Namespace = "http://www.yoursite.com/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class AutocompleteTagData : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
// Note the query strings passed by jquery autocomplete:
//QueryString: {q=a&limit=150×tamp=1227198175320}
LookupTagCollection tags = Select.AllColumnsFrom<LookupTag>()
.Top(context.Request.QueryString["limit"])
.Where(LookupTag.Columns.TagDescription).Like(context.Request.QueryString["q"] + "%")
.OrderAsc(LookupTag.Columns.TagDescription)
.ExecuteAsCollection<LookupTagCollection>();
foreach (LookupTag tag in tags)
{
context.Response.Write(tag.TagDescription + Environment.NewLine);
}
}
public bool IsReusable
{
get
{
return false;
}
}
}