When using ASP.NET on the web server some things become easier because
it brings a built-in framework for reusing HTML code fragments, buildingcontrols and components and managing some useful things around it. I will
use some features of the page model, ASP.NET user controls and Web
Controls here to make it easy building AJAX Controls.
Together with the JavaScript Behaviours that are used to build the client side
the ASP.NET Controls on the server is a solid basis for building AJAX
Controls.
There are good articles on the web that explain how to build this kind of
controls and there are a lot of samples too. Most of these examples don’t use
client side functionality but rely on the usual reload mechanism by using
forms. There are some tricky things around when building controls that need
the specific JavaScript on the client too.
User Controls
The very useful functionality of ASP.NET User Controls is the possibility of
writing often needed HTML code only once in an *.ascx file and reuse it as
often as needed by writing only referencing tags into the ASP.NET pages.
Web Controls
Some things are hard to be done when using User Controls. Especially when
the Control is only a framing element for more inner HTML objects User
Controls cannot be used easily. In this case Web Controls are used to
implement the specific tag that have to be implemented as special classes, are
easier to be implemented so you can find both technologies in the samples.
When using Web Controls or User Controls in ASP.NET pages it is necessary
to declare the namespace, tag name and implementation of any server tag that
is used. When including a reference by dragging a control onto the page this
directive is included at the top of the page. If you use the same tags multiple
times in your web application you can also use the declaration mechanism that
is available through the web.config file in the web application root folder. So
if you do not find the declarations have a look at web.config.
Delivering controls to the browser
Delivering a JavaScript enabled control to the client consists of the following
elements:
• The global JavaScript include file jcl.js must be present on the client. It
must be included once by referencing to it using a <script> element in the
header of each page.
• The JavaScript include file of the control that contains the control’s
Behaviour implementation must be present on the client. It must be
included once only even if multiple controls of the same type are used on
the same page.
• The control’s html code has to be included for every instance. The
parameters that are specified in the server side declaration have to be
brought to the client for make them available to the Behaviour
implementation.
• The JavaScript Behavior must be bound to the generated HTML element
by calling the jcl.LoadBehaviour method once for every instance of the
control
Of course it is possible to bring all these components to the client without
using a web control but some server side logic implemented by the
programmer who implements the control can help hiding these details from
the programmer who uses the control.
Registering Script includes
A very useful server-side functionality of ASP.NET Web Forms is the way
how controls can control that a JavaScript include file is needed for a
control to work successfully. The function "RegisterClientScriptBlock" that
is available on the Page object as well as on the new
Page.ClientScriptManager object in ASP.NET 2.0 can be used to specify
some html code that will be included inside a HTML form element before
all other HTML content.
With "IsClientScriptBlockRegistered" it is possible to check if a include file
is already registered.
Using this a mechanism is perfect for building AJAX Controls because the
web programmer needs not to know about the control specific JavaScript
include files that must be included for a Control to work properly. He can just
include the AJAX controls at the right place and everything else gets
magically done.
Page.RegisterClientScriptBlock("CommonBehaviour",
"<script type='text/javascript' src='"
+ Page.ResolveUrl("~/controls/jcl.js ")
+ "'><" + "/script>\n"));
When having a HTML form element on the page the form element will
automatically include all the submitted scripts before all other contained
HTML content.
Registering Script includes without a form element
Using this trick, it makes no difference whether a HTML form element exists
in the page or not. This is important especially for AJAX applications because
If you want to use ASP.NET version
1.1 you need only include a HTML
form element before all other HTML
code to get the includes into the page.
This doesn't mean that the AJAX
Controls must be inside the form
element, they also may be positioned
after an empty form element.
the avoid using any form functionality and get rid of reloading the whole
page.
When using ASP.NET 2.0 you can use a little trick to get the HTML script
tags for the include files added to the end of the HTML head element.
The <head> element must be marked with "runat=server" to make this trick
work. If the "runat=server" is missing on the page the Page.Header object will
be set to null and it is not possible to dynamically add new nested controls.
// register the JavaScripts includes without need for a HTML form.
if (!Page.ClientScript.IsClientScriptBlockRegistered(Page.GetType(),
"CommonBehaviour")) {
Page.ClientScript.RegisterClientScriptBlock(Page.GetType(),
"CommonBehaviour", String.Empty);
Page.Header.Controls.Add(new LiteralControl(
"<script type='text/javascript' src='"
+ Page.ResolveUrl("~/controls/jcl.js ")
+ "'><" + "/script>\n"));
} // if
The RegisterClientScriptBlock function is called by using an empty string
value. This makes it possible to detect within other controls, that the
JavaScript file is already included. The first instance of a control is the one
that effectively includes this marker and additionally includes a LiteralControl
that contains the script file reference.
Because the jcl.js file is required by all the web controls it is important that
the same type and name is use in every control. I use Page.GetType() and
“CommonBehaviour” for jcl.js.
For the JavaScript files that contain the Behaviour implementations the
controls type is used through this.GetType() and the name is always
“MyBehaviour”.
The script fragment that binds the Behaviour to the html element is included
into directly just after rendering the html code of the control:
<script defer="defer" type="text/javascript">
jcl.LoadBehaviour("<%=this.UniqueID %>", PropInputBehaviour);
</script>
Parameters
Specifying parameters that control the specific functionality of the AJAX
Control are part of the specific source code of a page by using attributes on
the tag of the control:
<ajax:Lookup ... LookUpService="OrteLookup" ... />
What you see here is NOT HTML code but a descriptive declaration of
rendering some HTML code here. When using ASP.NET User Controls these
attributes do not get automatically rendered as HTML attributes. Instead the
ASP.NET framework matches them to properties of the class that builds the
control on the server. So any attribute that is used as a parameter must also be
defined as a field or property of the control’s class to make the value available
on the server.
<%@ Control Language="C#" ... %>
<script runat="server">
public string lookupservice = "DefaultService.asmx";
...
</script>
To make it available on the client the values of these members must then be
written out in the HTML code that is generated by this control:
<input ... lookupservice="<%=this.lookupservice %>" ... />
The consequence of this is that the default-values for parameters that are not
specified in the source code must be specified in the initialization of the class
members and that values assigned to in the JavaScript prototype objects are
always overridden.
HTML Code
Writing specific HTML code for a User Control is simply done by writing it
down at the end of the *.ascx file. It can be as complex as you like it to be.
Be sure to also add the unique id of the control into the generated HTML
code:
id="<%=this.UniqueID %>"
An ASP.NET User control doesn’t automatically create an outer HTML
object. It is also possible to generate multiple objects in a row. In this case the
JavaScript behavior is attached to the object that is assigned the unique id.
If you need a reference to another web resource you can use the ResolveUrl
method of the Page object:
src="<%=Page.ResolveUrl("~/controls/images/drop.gif") %>"
Programming the Behaviour
The specific JavaScript behavior that should be used to implement the client-
side functionality for a User Control should be implemented in a separate
JavaScript include file. This is not strictly necessary but is good for the overall
performance because it can be cached in the browser.
I use the same name as the *.ascx file for this control specific include file and
place them all into the ~/controls folder.
To attach the behavior to the html object a small JavaScript fragment is also
part of the rendered HTML code:
<script defer="defer" type="text/javascript">
jcl.LoadBehaviour("<%=this.UniqueID %>", LookUpBehaviour);
</script>
Registering the script includes
Before the HTML text is send to the client all the JavaScript include files that
are needed by the control must be registered on the page. This can be done in
the OnPreRender method:
protected override void OnPreRender(EventArgs e) {
base.OnPreRender(e);
...
// register the JavaScripts includes without need for a Form.
if (!Page.ClientScript.IsClientScriptBlockRegistered(Page.GetType(),
"CommonBehaviour")) {
Page.ClientScript.RegisterClientScriptBlock(Page.GetType(),
"CommonBehaviour", String.Empty);
((HtmlHead)Page.Header).Controls.Add(new LiteralControl(
"<script type='text/javascript' src='"
+ Page.ResolveUrl("~/controls/jcl.js")
+ "'><" + "/script>\n"));
} // if
if (!Page.ClientScript.IsClientScriptBlockRegistered(this.GetType(),
"MyBehaviour")) {
Page.ClientScript.RegisterClientScriptBlock(this.GetType(),
"MyBehaviour", String.Empty);
((HtmlHead)Page.Header).Controls.Add(new LiteralControl(
"<script type='text/javascript' src='"
+ Page.ResolveUrl("~/controls/LookUp.js")
+ "'><" + "/script>\n"));
} // if
} // OnPreRender
All the code fragments can be found in many web controls implemented in the
~/controls/*.ascx and the ~/App_Code/*.cs files.
Integration into ASP.NET
No Form element
AJAX controls are built for NOT posting back, submitting the changes the
user did to the server by using the HTML form element and reloading the
whole page. That’s why we want AJAX in our web applications. In contrary,
ASP.NET controls are made for exact this kind of client-server interaction and
therefore are most of the time placed inside a HTML form element. Because
we do not use autopostback functionalities nor do we directly implement some
script to call form.submit() we do not get problems with a possible existing
HTML form element.
If you plan to extend your existing ASP.NET Web Forms with AJAX
Controls – which is one of the scenarios I want to support – you will need this
HTML form element. If you build new web pages without using the server
side functionality of ASP.NET web forms you can eliminate this HTML form
element completely.
The Return key
One thing will disturb users when a form element is present: pressing the
<return> or <enter> key in an input field is (like F5) reloading the page. All
information that came to the page since the time it was loaded and displayed
by using some asynchronous communications will be lost too.
Pressing return on input-fields outside a HTML form element normally has no
visible effect because the return character will not be added to the value of the
HTML input element.
There is a simple trick built into the jcl.js file that can help you to get around
this situation. By capturing all keypress events and test if return was pressed,
it is possible to cancel the keypress event early and prevent the automatic call
of the submit functionality.
To enable this trick for the input fields they just must have an attribute
"nosubmit" set to "true". You can add this attribute to the HTML code or add
it to the JavaScript behavior bound element too.
Tags:
JavaScript