
Introduction
This article goes over integrating AJAX into your website without using Atlas or
controls that are already wired up for you. This is useful for
environments where you can not use Atlas. It's also interesting if you'd like a
lower level look at how asynchronous client server communication works and the
issues involved. The techniques covered include embedding javascript in
the web application's assembly, registering variables from C# codebehind, and
using AJAX to communicate with C# server side code.
Using the code
To deploy the web application, just unzip the AJAXIntegration.zip into the
inetpub\wwwroot directory. Then use the IIS snap in to create an application on
the AJAXIntegration directory.
Project
One of the most common AJAX applications is an autocomplete text box. These are
useful for maintaining data integrity and keeping input forms relatively
uncluttered and intuitive. The key component is a nice javascript library from
http://script.aculo.us/. This has, among other neat features, a good
autocomplete implementation.
Points of Interest
The first thing we create is a web user control with a textbox and the necessary
decoration and methods to hook into the scriptaculous library. I saved
the scriptaculous library right in the root of my web project in a folder
called Scriptaculous. The asp.net code for the user control looks like
this:
<asp:TextBox id="TextBox1" runat="server" Width="300px">
</asp:TextBox>
<div id="TextBoxUpdate" runat="server" style="BORDER:
#888 1px solid; DISPLAY: none; WIDTH: 250px;
CURSOR: pointer; POSITION: absolute; HEIGHT: 300px;
BACKGROUND-COLOR: white"></div>
Notice how there's no mention of the javascript. All the magic is in the
codebehind, in the PreRender event. This event is called after most of the work
to build the page is done. It is the right place to register client side
scripts:
private void AutocompleteTextBox_PreRender(object sender,
System.EventArgs e)
{
this.Page.RegisterArrayDeclaration(
"Tallan_AutocompleteTextBox","'"+
SearchBox.ClientID+"','"+SearchBoxUpdate.ClientID+
"','"+"http://localhost/AJAXIntegration/Fetch.aspx
?parameterId="+Parameter+"'");
string commonScriptKey = "AutocompleteTextBox";
if ( !Page
.IsClientScriptBlockRegistered(commonScriptKey) )
{
using (System.IO.StreamReader reader =
new System.IO.StreamReader(
typeof(AutocompleteTextBox).Assembly
.GetManifestResourceStream(typeof(
AutocompleteTextBox),
"AutocompleteTextBox.js"))
)
{
String script = "<script language="'javascript'"
type='text/javascript' >\r\n<!--\r\n" +
reader.ReadToEnd() + "\r\n//-->\r\n</script>";
this.Page.RegisterClientScriptBlock(
commonScriptKey, script);
this.Page.RegisterStartupScript(commonScriptKey,
"<script language="'javascript'" type='text
/javascript' >\r\n<!--\r\n
Tallan_AutocompleteTextBox_Init(); \r\n//-->
\r\n</script>");
}
}
}
The key technique here is using Page.RegisterArrayDeclaration, Page.IsClientScriptBlockRegistered,
Page.RegisterClientScriptBlock and Page.RegisterStartupScript
to handle the dynamic nature of asp.net user controls. What I mean by this
is that a user control can be deployed on any page under any number of parent
controls. Therefore its id will be vary depending on where it resides. However,
C# codebehind has access to that id, namely [some control].ClientId.
So when the script is registered to the page, the javascript can operate with
the correct controls on the client side. The Page.RegisterArrayDeclaration
method will create a new javascript array in the html code that is
pushed to the client. If the array exists, it will append to
it. The other register methods are used to only register the script if
one is not already present. This will be useful when you want to use more
than one control on the same page, as I will in the example.
The other point of interest in the above code is how the scripts are fetched to
be registered. In the Page.RegisterStartupScript call I
used inline text which will get written to the client as
is. In the Page.RegisterClientScriptBlock however I
used a StreamReader to access an "Embedded Resource". The way this works
is you make a javascript file and in Solution Explorer right click and choose
Properties. In the Build Action property, select the Embedded Resource
choice. Now when you compile the project, this text file will be
available to read as is shown above. As a little side note, if your
javascript changes from these embedded resources aren't propagating to your
deployed application, try rebuilding the project and redeploying it. This
will make sure you have the freshest available copy of the script in the
assembly.
The javascript file AutocompleteTextBox.js that is registered to the page
contains an initialization method which is called by the page upon
startup. It is this method that assigns the AJAX behavior to the
textbox:
function Tallan_AutocompleteTextBox_Init() {
try
{
if (!Tallan_AutocompleteTextBox_BrowserCapable())
{ return; }
for(var i=0;i<Tallan_AutocompleteTextBox.length;
i+=3 ){
new Ajax.Autocompleter(
Tallan_AutocompleteTextBox[i],
Tallan_AutocompleteTextBox[i+1],
Tallan_AutocompleteTextBox[i+2], {
paramName: "search" });
}
}
catch(e){}
}
Basically the whole linkup is just instantiating a
new Ajax.Autocompleter
The constructor takes three parameters:
- the client id of the text box to autocomplete
- the client id of a div that will pop up and display the results
- the url of a resource that will pass back autocomplete choices
- the parameters for various scriptaculous options
The last thing to go over is the resource that supplies autocomplete choices.
You can look up the scriptaculous parameters on their website as they continue
to release updates and have a good documentation wiki.
Scriptaculous AJAX looks for a simple page that renders only an html list of the
form:
<ul><li>autocomplete choice 1</li>
<li>autocomplete choice 2</li></ul>
This
is where we can create an aspx page (I called it Fetch.aspx) to hook into
some server data source and render dynamic or cached autocomplete
content. First create a new web form in your project and make sure to
delete everything in the generated source besides the page tag. This
includes the form tag, html tag, everything. The reason for this is that
Internet Explorer won't display the popup autocomplete div if the page is more
complicated than just a simple <ul> list. Then you're ready to
write the following code in the Page_Load event:
string match = Request.Form["search"].ToLower();
Response.Write("<ul>");
foreach( string s in sample )
{
if( s.ToLower().IndexOf(match) == 0 )
{
Response.Write("<li>"+s+"</li>");
}
}
Response.Write("</ul>");
Notice how we get the "search" parameter from the Request.Form object.
The name of this parameter is configured in the Ajax.Autocompleter constructor
which is called in the Tallan_AutocompleteTextBox_Init function discussed
above. The sample array is just a string[] of sample
data. You can easily get your data from a DataTable using the
Select method. In the AutocompleteTextBox control I also included a
property called Parameter which ends up being passed to Fetch.aspx in the query
string like:
http://localhost/AJAXIntegration/Fetch.aspx?parameterId=[Parameter].
You can access this and any other properties you wish to pass from each
individual control via the Request.QueryString object.
One last thing to note is that the SmartNavigation attribute confuses
InternetExplorer's asynchronous request, at least when it comes to
scriptaculous' implementation. Therefore, disable SmartNavigation on any
page where the control will be used.
Conclusion
Now just deploy the code and try it for yourself. You can also open the project
in Visual Studio or Sharp Develop and browse the code. Remember that you
can enable script debugging in Internet Explorer by going to Tools, Options,
Advanced and unchecking the "Disable script debugging" option. Doing so
will allow you to place break points in the js files from the Scriptaculous
folder and see what's going on during the asynchronous calls. I did not cover the full implementation details for
the sake of brevity. Therefore, please don't hesitate to ask questions.