Monday, April 21, 2008

Autocomplete, Jayrock, JQuery, ASP.net...

***WARNING: Not a beginner article, but not an advanced one either. Start at your own risk, I will do my best to answer questions via comments.***

What?
I have put together an autocomplete using an ASP.net ashx (handler) webservice with Jayrock and JQuery. So I'm sure you are wondering what the big deal is... Well, first off, Jayrock uses JSON-RPC and its own proxy to tunnel the Asynchronous request to the webservice and this caused some problems with most of the JQuery autocompletes I tried.

Why?
Well, for two reasons. One, it was a pain to finally get it to work (although now it seems trivial and I would like the search engines to pick this up to help others, if there are others) and two, because I set a goal of doing one good post per day (man this is a huge goal!).

How?

First create a new ASP Handler *.ashx, I called mine Lookup.ashx.
Next, you need to download: JQuery, Jayrock (**Make sure to get the Lastest builds**). Add the Jayrock DLL's as References to your Project.
Below is my Function for Wildcard searching (FYI: I put this into another class **NOTE** This returns a datatable, no db schema is proved, so you will have to figure this out) and my Lookup.ashx is below that.



Public Function GetUser(ByVal Search As String) As DataTable
Dim strQuery As String

Dim dt As New DataTable
Dim words() As String = Split(Search, " ")

strQuery = "SELECT LTRIM(RTRIM(First_Name)) + ' ' + LTRIM(RTRIM(Last_Name)) AS FullName, LTRIM(RTRIM(ISNULL(Login, ''))) As UserID, LTRIM(RTRIM(ISNULL(Job_Title, ''))) As Job " _
& " FROM Media.dbo.Employee e " _
& " WHERE 1 = 1 AND Login IS NOT NULL AND Login <> ''"

Dim tempStr As String
Dim i As Integer

For i = 0 To UBound(words)
tempStr = " AND ((e.First_Name LIKE '%" & words(i) & "%') OR (e.Last_Name LIKE '%" & words(i) & "%') OR (e.Login LIKE '%" & words(i) & "%'))"

If Len(words(i)) > 0 Then
strQuery += tempStr
End If
Next


Dim myConn As New System.Data.SqlClient.SqlConnection(ConfigurationManager.ConnectionStrings("ConnectionString").ToString)
Dim myCmd As New System.Data.SqlClient.SqlCommand(strQuery, myConn)

Try
myConn.Open()
Dim myReader As System.Data.SqlClient.SqlDataReader
myReader = myCmd.ExecuteReader
dt.Load(myReader)
Finally
myConn.Close()
End Try

Return dt
End Function
Here's the Lookup.ashx
<%@ WebHandler Language="VB" Class="Lookup" %>

Imports System
Imports System.Web
Imports Jayrock.Json
Imports Jayrock.JsonRpc
Imports Jayrock.JsonRpc.Web

Public Class Lookup
Inherits JsonRpcHandler

_
Public Function Lookup(ByVal q As String) As Collection
Dim Users As New Collection()
Dim myRequest As New RequestClass()

Dim User As New UserEmployee

Dim dr As DataRow
Dim dt As DataTable

dt = myRequest.GetUser(q)

For Each dr In dt.Rows
User.FullName = dr("FullName")
User.UserID = dr("UserID")
User.Job = dr("Job")
Users.Add(New UserEmployee(User))
Next

dt.Dispose()

Return Users
End Function

End Class

Public Class UserEmployee
Public FullName As String
Public UserID As String
Public Job As String

Public Sub New()

End Sub

Public Sub New(ByVal User As UserEmployee)
Me.FullName = User.FullName
Me.UserID = User.UserID
Me.Job = User.Job
End Sub
End Class

Next create a standard ASP.net page *.aspx


In the <head> section of your aspx page, add:

<style type="text/css">
.suggestionsBox {
position: relative;
left: 30px;
margin: 10px 0px 0px 0px;
width: 200px;
background-color: #212427;
-moz-border-radius: 7px;
-webkit-border-radius: 7px;
border: 2px solid #000;
color: #fff;
}

.suggestionList {
margin: 0px;
padding: 0px;
}

.suggestionList li {
margin: 0px 0px 3px 0px;
padding: 3px;
cursor: pointer;
}

.suggestionList li:hover {
background-color: #659CD8;
}
</style>
<script type="text/javascript" src="Lookup.ashx?proxy"></script>
<script type="text/javascript" src="js/json.js"></script>
<script type="text/javascript" src="js/jquery-1.2.3.js"></script>

<script type="text/javascript">
function jQueryChannel() {
this.rpc = function(call) {
if (!call.callback)
throw new Error('Synchronous calls not supported.');
$.ajax({
type: "POST",
url: call.url,
data: JSON.stringify(call.request),
beforeSend: function(xhr) {
xhr.setRequestHeader("X-JSON-RPC", call.request.method);
},
success: function(s) {
call.callback(JSON.eval(s));
}
});
}
}

function suggest(inputString) {
if(inputString.length > 3) {
var lookup = new Lookup(); //Instantiates new Jayrock Proxy from *.ashx?proxy
lookup.channel = new jQueryChannel(); // Overrides default Jayrock Proxy Channel

lookup.Lookup(inputString, function(data) {
//$('#test').html("Length: " + data.result.length + " Test" + dump(data.result));
var results = data.result;

if (results.length > 0) {
var html = "";

//html += "<ul>";
for(var i = 0; i < results.length; i++) {
html += "<li onclick=\"fill('" + results[i].userID + "');\">" + results[i].fullName + "<br />(" + results[i].job + ") " + "<br />" + results[i].userID + "</li>";
}
//html += "</ul>";

$('#autoSuggestionsList').html(html);
$('#suggestions').show();
}
});
} else { // Hide the suggestion box.
$('#suggestions').hide();
}
} // lookup

function fill(value) {
$("#ctl00_ContentPlaceHolder1_txtUserID").val(value);
setTimeout("$('#suggestions').hide();", 200);
}
</script>
In the body of your aspx page, add a textbox and then modify it as shown below.
<div>
<label>
<b>UserID:</b>
<asp:TextBox ID="txtUserID" runat="server"></asp:TextBox><span style="font-size: 16pt">
<asp:Label ID="lblUserID" runat="server" Font-Size="12pt"></asp:Label>
</span>
</label>
<div class="suggestionsBox" id="suggestions" style="display: none;">
<img src="images/autocomplete_upArrow.png" style="position: relative; top: -12px; left: 30px" alt="upArrow" />
<div class="suggestionList" id="autoSuggestionsList"></div>
</div>
</div>
In your code behind (*.aspx.vb) add to the Page_Load function:
txtUserID.Attributes.Add("onkeyup", "suggest(this.value);")
txtUserID.Attributes.Add("onblur", "fill();")
This adds 'onkeyup="suggest(this.value);" onblur="fill();"' to your ASP.net Textbox

Now, unless I missed something, you should have a working fast, simple, autocomplete using JQuery (awesome Javascript framework, Jayrock (easy to build JSON webservices with), and JSON (lightweight).

To be fair, I must give credit, where credit is due...

Thanks go to the creators of JQuery, Atif Aziz (the creator of Jayrock) and his response on Google Groups for JayRock and JQuery as well as
Jamie at http://nodstrum.com/2007/09/19/autocompleter/ who's autocomplete I bastardized.

Sunday, April 20, 2008

Sex, Drugs, and... Computers?

Welcome to my new blog.

I am Michael Rice - President, Eferi and Chief Architect (Applications)
My background is in enterprise business application architecture (data structures and user interfaces) and consulting.

Corporate Consulting Gigs:
Zillow, Consumer Affairs, and a few others off the grid

Specialties: Real Estate, Finance, Health care. What do they have in common? On the business end, not much, in the I.T. sector, more than you would think, e.g. transaction processing, interfaces (XML, etc.), and bad user interfaces. :-)

Personal:
I live in Johnson City, TN. with my wife, brand new baby (born March 25th, 2008) and dachshund. Great air, great people, and great lakes!

This blog:
My goal for this blog is to provide examples, dialog, and comments on the direction of enterprise architecture... as well as other interests of mine.

I hope you enjoy the ride, I hope to make it a long one.