Wednesday, October 14, 2009

JQuery Auto-complete Based On SPList Items In SharePoint Environment

Step 1. Download latest jQuery and jQuery Auto-complete plugin libraries.

Step 2. Included jQuery, auto-complete plugin js and its css files in the Page. This can be done in ASP.NET page, Master page, Control or WebPart:
    if (!Page.ClientScript.IsClientScriptBlockRegistered("jQueryScript"))
{
string path = "<script type='text/javascript' language='javascript'
src='/_layouts/Autocomplete/jQuery.js'></script>"
;
Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "jQueryScript", path);
}
if (!Page.ClientScript.IsClientScriptBlockRegistered("AutocompleteScript"))
{
string path = "<script type='text/javascript' language='javascript'
src='/_layouts/Autocomplete/jquery.autocomplete.js'></script>"
;
Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "AutocompleteScript", path);
}
if (!Page.ClientScript.IsClientScriptBlockRegistered("AutocompleteCss"))
{
string path = "<link rel='stylesheet' type='text/css'
href='/_layouts/Autocomplete/jquery.autocomplete.css' />"
;
Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "AutocompleteCss", path);
}

Step 3: Add following script on the page to enable the auto-complete on the textbox (WebUrl, ListName, FieldName need to be set in the code behind, or hard-coded in the script):
<script type="text/javascript">
$(document).ready(function () {
var handlerUrl = "/_layouts/Autocomplete/AutoCompleteHandler.ashx";
var request = handlerUrl + '?WebUrl=<%= WebUrl %>&ListName=<%= ListName %>&FieldName=<%= FieldName %>';
var options = { max: 20, multiple: true, multipleSeparator: ';' };
$("textarea[id='" + '<%= txtBox.ClientID %>' + "']").autocomplete(request, options);

});
</script>

Step 4: Add javascripts, css file, and the auto-complete HttpHandler inside the layout folder (C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\TEMPLATE\LAYOUTS\Autocomplete\). The auto-complete HttpHandler includes two parts:

AutoCompleteHandler.ashx:
<%@ Assembly Name="Project assembly fully qualified name" %>
<%@ WebHandler Language="C#" Class="AutoCompleteHandler" %>

AutoCompleteHandler.ashx.cs:
using System;
using System.Web;
using System.Collections.Generic;
using System.Text;
using Microsoft.SharePoint;

public class AutoCompleteHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
string prefixText = string.Empty;
string inputText = context.Request["q"];
string webUrl = context.Request["WebUrl"];
string listName = context.Request["ListName"];
string fieldName = context.Request["FieldName"];
if (!string.IsNullOrEmpty(inputText))
{
int lastDelimiter = inputText.LastIndexOf(";");
lastDelimiter = (lastDelimiter > 0) ? lastDelimiter : 0;
prefixText = inputText.Substring(lastDelimiter).TrimStart(new char[] { ',', ';', ' ' });
}
if (string.IsNullOrEmpty(prefixText) || string.IsNullOrEmpty(webUrl)
|| string.IsNullOrEmpty(listName) || string.IsNullOrEmpty(fieldName))
{
return;
}

SendAutoComplete(context, inputText, webUrl, listName, fieldName);
}

private void SendAutoComplete(
HttpContext context,
string prefixText,
string webUrl,
string listName,
string fieldName)
{
List<string> nameList = new List<string>();
try
{
SPList list = SPListHeler.GetList(webUrl, listName);
if (list != null)
{
SPQuery spQuery = new SPQuery();
string query = @"<Where><BeginsWith>
<FieldRef Name='$NAME$'/>
<Value Type='Text'>$TEXT$</Value>
</BeginsWith></Where>"
;
spQuery.Query = query.Replace("$NAME$", fieldName).Replace("$TEXT$", prefixText);
int limit;
if (int.TryParse(context.Request["limit"], out limit) && limit > 0)
spQuery.RowLimit = (uint)limit;
SPListItemCollection items = list.GetItems(spQuery);
foreach (SPListItem item in items)
{
nameList.Add(item[fieldName].ToString());
}
}

nameList.Sort();
StringBuilder sb = new StringBuilder();
foreach (string tag in nameList)
{
sb.Append(tag + Environment.NewLine);
}

context.Response.Cache.SetCacheability(HttpCacheability.NoCache);
context.Response.ContentType = "text/plain";
context.Response.Write(sb.ToString() + " ");
}
catch (Exception ex)
{
// Log error
}
}

public bool IsReusable
{
get { return false; }
}
}

Note: To improve the performance, we can cache all the names and query the memory objects inside the http handler instead of CAML query each time.