Montag, September 19, 2005

Connecting Controls

Separating and encapsulating the functionality into components helps to build a reusable framework. But most components do not exist alone but must be linked to other elements.

The classical approach

When using the standard built-in controls of ASP.NET you will use the events that are exposed on buttons, fields and the page and write small methods that response to these events. Inside these event handlers you reference to other objects directly because they are part of the page or control class or by using their id or at runtime unsing a search method.

These handlers are executed by using a submit() of the HTML form element - a post-back situation that should not happen in AJAX applications. We need a solution on the client side of the application in JavaScript.

You can also follow this approach on the client by writing event handlers and attaching them to the native HTML events of the HTML objects.

The drawback of these architectur is that when using complex controls the object that exposes this event might be an inner part of the control without an id or maybe not existing while the page is still loading.

If you want to follow this approach you can use jcl.AttachEvent. This is a method from jcl.js include file that you can use for this purpose and that works across the browser platforms and that allows multiple event registrations..

Even when you know all inner details of the actual control you might not want to use that knowledge because you will give away the chance to extend or re-implement a control without also changing all the implementations on the pages.

The WebPart approach

With Sharepoint and the ASP.NET 2.0 WebPart Framework came another mechanism of connecting components by using a provider-subscription pattern. Components can register themselves for publishing (provider) or consuming (subscription) a specific property-value. Every time when the value of a property changes in a provider all controls that registered a subscription for this property are informed about that.

There are some advantages for this approach:

  • It works even if the controls that build up a page are not known at compile-time. (Sharepoint)
  • Multiple controls can easily publish the same property.
  • Multiple controls can easily consume the same property.
  • We do not need any browser- or object-specific events.
  • The IDs of the objects must not be known.
You see that this is a higher level approach that fits better into the problem space of component based pages and portals and it can in addition be combined with the eventing model of the native HTML objects.

The Connection Test Sample

This sample uses 3 kinds of controls implemented with JavaScript Behaviours to show (and test) the implementation and usage of the client side controls connections. There are 3 properties used in this sample (x, y and z) that can be modified by 2 different Controls and that are visualized by a simple bar chart.

See: http://www.mathertel.de/AJAXEngine/S03_AJAXControls/ConnectionsTestPage.aspx

PropInput Control

This control renders a HTML input element that is extended by a JavaScript Behaviour to raises a change of its value to the property that is specified by the "name" attribute.

This control also registers itself as a consumer to display the actual value when changed by another control.

PropHSlider

This control implements is a horizontal moveable rectangle that acts as a slider. It can be used to change a property value that is specified by the "name" attribute in the range from 0 to 100.

This control also registers itself as a consumer to display the actual value when changed by another control.

PropBarChart

This control implements a simple bar chart to display multiple values. The names of the properties are displayed below the bars.

The names of the values can be specified by the "properties" attribute by using a semicolon separated list of names.

The max displayable number can be specified by the "maxvalue" attribute. This value is used as a scale factor for the display.

PropEventLog

This control logs every change of any property and lists the new value on the page.

DataConnections Reference

The names of the properties are always converted to lowercase characters so they should only be compared by after a toLowercase conversion.

DataConnections.RegisterProvider(obj, propName)

An object that provides a property must register itself using this function.

The name of the property set to a '*'-character to register for all existing properties.

DataConnections.RegisterConsumer(obj, propName)

An object that wants to be informed about the values of a property must register itself using this function.

The name of the property can be specified by a star-character to register for any properties that exist on the page.

DataConnections.Raise(propName, propValue)

This function must be called to raise the change of a property. All registered objects for this property get their GetValue method are called immediately.

DataConnections.GetPropValue(propName)

This function can be used to poll the actual value of a property. This eliminates the need for implementing a array of the current values of the properties an multiple controls.

DataConnections.PersistPropValue(propName)

Using this function a property can be persisted into a cookie value and will be raised when the page loads again. This help a lot for surviving page reloads.

The used cookie is not a permanent cookie so the content will be not available after the browser was closed.

control.GetValue(propName, propValue)

This function must be implemented by a control to receive the notification changes.

Freitag, September 16, 2005

Anatomy of an AJAX Control

AJAX Web Control architecture

Writing an AJAX enabled control (here AJAX Control) is as easy as writing another AJAX enabled web applications. The only difference lies in the kind of separating HTML, JavaScript and the clueing stuff like AJAX Actions and Server calls into the right places so that the control can be reused in other places.

If you just want to separate a part of your application so that other members of the team can work on it separately then you can place all the code of the control into the User Control’s *.ascx file.

You should be familiar with the technique of the <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.

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 behaviour 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 behaviour 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 behaviour 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 >

AJAX Actions

The AJAX Action that is used by the Control can be declared as a part of the Behaviour’s prototype object. In the LookUp Control you can find the _fillAction property that is the declaration object of the AJAX Action.

// declare an AJAX action to the lookup service
_fillAction: {
  delay: 100,
  prepare: function(fld) { fld.savevalue = fld.value; return (fld.value); },
  call: null, // is assigned later
  finish: function (val, fld) {
    if (fld.savevalue != fld.value) {
      ajax.Start(fld._fillAction, fld); // again
    } else {
      var dd = fld.CreateDropdown(fld);
      fld.FillDropdown(dd, val);
    } // if
  }, // finish
  onException: proxies.alertException
}, // _fillAction

If you only have one single instance of the Control on the page you will not get into trouble. If there are multiple instances of the Control on the same page these actions definitions will be shared by all controls so you better make a copy of it using the jcl.CloneObject function because they will use different WebServices.

The reference to the WebService should also be set in the init function after the page was loaded because it is not guaranteed whether the WebService proxies are already setup correctly when the behaviour is attached.

this._fillAction = jcl.CloneObject(this._fillAction);
this._fillAction.call = proxies[this.lookupservice].GetPrefixedEntries;

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
p>All the code fragments can be found in the third part of the samples:

OrteLookUp.aspx is the implementation of the page,

~/controls/LookUp.ascx is the first visible control and

~/controls/LookUp.js is the corresponding JavaScript Behaviour.

Mittwoch, September 14, 2005

AJAX Engine documentation update

Today I updated the documentation of the AJAX Engine.

See http://www.mathertel.de/AJAX/Aspects%20of%20AJAX_index.htm.

It’s not necessary any more to read the articles from the end to the beginning because they layer upon each other partially.

ATLAS

The AJAX Framework from Microsoft called ATLAS was published yesterday. I am glad to see that Microsoft follows my idea of generating proxies for WebServices.

They use a JSON syntax and not SOAP or WSDL for the communications so it’s not very portable to other server-side platforms.

Dienstag, September 13, 2005

Using ASP.NET

When using ASP.NET on the web server some things become easier because it brings a built-in framework for reusing HTML code fragments, building controls 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 I wrote about some days ago and that are used to build the client side the ASP.NET Controls on the server are 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. But there are some tricky things around too to reuse this for building AJAX controls.

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. Here some Web Controls, that have to be implemented as special classes, are easier to be implemented so you can find both technologies in the samples.

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 or 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 behaviour bound element too.

Common JavaScript

The AJAX Controls are using the JavaScript behaviours and therefore need all the common include file "~/controls/jcl.js " and most of them also need a specific include file containing the specific JavaScript behaviour prototype.

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 sucessfully. 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 such 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.

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.

Registering Script includes without a form element

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 that should be marked with "runat=server".

// 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

Using this trick, it makes no difference whether a HTML form element exists in the page or not.

Samples and Downloads

There are already some samples for AJAX controls posted on the Samples Web Site. I have added a portal page for the project. Just open the [Samples] tab and look into the third part of the samples collections.

You can also download the whole project - as it is. It’s made for ASP.NET 2.0 Beta 2 build but it also works with the August drop.

Dienstag, September 06, 2005

Caching with AJAX applications

AJAX applications offer better response times and are faster (or at least seems to be faster) that traditional web applications.

The main reason behind this is the separation of the initial page loading from the loading of additional data and the absence of reloading this page again and again when the data of the page changes.

Building up a page works by using conventional call of a URL using the http-get mechanisms. Calling the server in the background by using asynchronous XMLHttpRequests is the second and often repeated part.

Caching can help to speed up both kinds of server requests and the best results can be bought out by preventing calls to the server.

Caching the initial page download

The page that is downloaded by navigating to the URL can be improved most effectively using the client-side cache features of the web browser. After adding the right http headers the browser will not ask the server for the specified time for new versions of the url-resource and will rely on the bytes that can be found in the client-side cache. Some things must be taken care of to make this working properly.

There is a very useful tool for windows from Eric Lawrence called fiddler available at http://www.fiddlertool.com/. He also wrote a good article on tuning and the http protocol: http://www.fiddlertool.com/Fiddler/help/http/HTTPPerf.mht.

Also http://msdn.microsoft.com/library/default.asp?url=/workshop/author/perf/perftips.asp is worth reading.

  • The initial page download MUST NOT contain any data or information that changes frequently or must be available to the client very soon after it changes. If any change of this data is initiated by a click or any other action on the client it is possible to force a request in this case even if the cache period has not ended yet. In this case you can live with a long caching period and an immediate change of the page.
  • The page MUST be designed to be a (almost) static resource from the view of the client.
    It however can vary for different users. When delivering personalized versions, the caching proxy servers as well as the caching features of the server must be turned off.
  • The smaller, the faster.
    I do not recommend using huge graphics and many inline style attributes. The Google applications show, that building fast web applications without many graphics is possible.
  • Use include files for JavaScript and CSS-files.
    Include files can be cached too on the client and can also be shared among different pages. It is good to use include files with common functionality or styles. Rarely ore once-only include files slow down the application.
  • Use only lowercase character in URLs.
    It is not obvious to windows users that page.aspx and Page.aspx are two different resources on the web. Even if the server (IIS and ASP.NET) treats these resources as equal, the client will retrieve and store them twice. The fact that makes them different is the kind of writing in the references in HTML tags "src" and "href" attributes. Because I am never sure how a reference is written in other places I prefer using lowercase character only.
  • Use the right http headers.
    For the IE there are the 2 special cache specific attributes pre-check and post-check that should be set correctly so that the IE does NOT even ask for a version but silently uses the version of the resources found in the client cache.

Caching the asynchronous requests

After the page is loaded there are more requests to the server now by using asynchronous calls in the background using the XmlHttpRequest objects.

When calling long running methods on the server for example complex SQL retrievals or expensive calculations is possible to instruct the server to cache the results and returning them without executing the same methods multiple times.

In ASP.NET you can use the CacheDuration property on the WebMethod attribute to specify the number of seconds the result may stay in the web server cache.

[WebMethod(CacheDuration=60)]

A simple sample on this can be found in the article at: http://support.microsoft.com/default.aspx?scid=kb;en-us;318299

The advantage in this approach is that all clients share the same cache and if there are multiple clients requesting the same service you might get very good response times.

It’s also possible to cache on the client. An approach that leads to less traffic on the net because repeating the same calls can be prevented. Http headers do not help in these situations because the request is not an http-get request and there is always a payload in the http body. Caching must therefore be done by some scripting on the client.

The caching feature in the JavaScript WebService proxy implementation can be enabled by calling the proxies.EnableCache method and passing the function that should further use caching. I added a button to the CalcFactorsAJAX.htm sample to how to enable show this:

proxies.EnableCache(proxies.CalcService.CalcPrimeFactors)

Also the TableData.aspx sample uses caching to prevent retrieving the same records multiple times.

By calling this method a JavaScript object is added that stored all results and is used to prevent a call to the server if an entry for the parameter already exists inside this object.

This is not a perfect solution, but it works under the following circumstances:

  • The parameter must be a string or number that can be used for indexing the properties of a JavaScript object.
  • The cache doesn’t clear itself. It can be cleared by calling EnableCache once again.
  • Only methods with a single parameter are supported.

Caching works good into AJAX applications and speeds up by preventing calculations, downloads and webserver calls.

Donnerstag, September 01, 2005

Project Site Update

This was a long week, not writing text but code. I updated the AJAX Aspects project side http://www.mathertel.de/AJAXEngine/ and I hope you find it more useful. It got more structure and there is now a good place to start the next steps.

  • I decided to clarify the conditions for using this work and added a statement and link to the Creative Commons Attribution 2.0 Germany License. Now it’s clear hat anyone can use it also for commercial projects. Please list my name in your license conditions.
  • I separated the pre-AJAX samples (part 1) from the AJAX samples (part 2). Old links might not work any more.
  • The third part of the project is building reusable AJAX components – AJAX Controls. Some samples are already on the side so if you are curious go and find them.
  • I added a "Impressum" and you can find my E-Mail address in there more easily. It’s a gif file so spiders do not reach me too easily.

I hope you like it :-)