Screenshot of "The iGoogle Demo" in the demo project
Introduction
This article assumes that you are familiar with ASP.NET WebForms. You might also want to know the basics about AJAX. This is a quote from Wikipedia on AJAX:
AJAX applications look almost as if they reside on the user's machine, rather than across the Internet on a server. The reason: pages get updated, not entirely refreshed.
“Every user action that normally would generate an HTTP request takes the form of a JavaScript call to the AJAX engine instead”, wrote Jesse James Garrett, in the essay that first defined the term. “Any response to a user action that doesn’t require a trip back to the server — such as simple data validation, editing data in memory, and even some navigation — the engine handles on its own. If the engine needs something from the server in order to respond — if it’s submitting data for processing, loading additional interface code, or retrieving new data — the engine makes those requests asynchronously, usually using XML, without stalling a user’s interaction with the application.”
Traditional web applications essentially submit forms, completed by a user, to a web server. The web server responds back by sending a new web page. Because the server must submit a new page each time, applications run more slowly and awkwardly than their native counterparts.
AJAX applications, on the other hand, can send requests to the web server to retrieve only the data that is needed, usually using SOAP or some other XML-based web services dialect. On the client, JavaScript processes the web server response. The result is a more responsive interface, since the amount of data interchanged between the web browser and web server is vastly reduced. Web server processing time is also saved, since much of it is done on the client.
AJAX is a maintenance nightmare
Today you have to create web applications that work in both IE and Firefox, and that is definitely doable but also pretty time consuming and a maintenance nightmare.
AJAX has become very popular, because it answers a need for speed and responsiveness in web applications and there has also been the "Google" effect:
"We create interactive web applications like Google does and they use AJAX too"
Yeah, but it is not so cool, if you use 95% of the time on testing in different browsers.
AJAX falls short on two major points: It has no support of the state management in ASP.NET Web Forms and it has no support for cross-browser web applications. The goal of this article is to solve these two issues.
Example
Let's say you have a tree-structure displayed to the user (like in Windows Explorer) and you only want to retrieve and display the sub-folders when the user expands a folder in the tree.
With AJAX you would call a web service to retrieve an array containing the subfolder names, links and icons and create the tree nodes using DHTML. You would have to create the web service methods, write cross-browser JavaScript to make it work and have hidden fields in your ASP.NET WebForm containing the state of the control.
What OutPost does is to perform a real postback through a Web Service that also manages the view-state (and keeps it on the web server). The InnerHtml
of the modified elements are sent back to the client and displayed in the browser. In that way the whole process is generalized and you don't need to create Web Service methods, DHTML JavaScripts or add hidden fields for your ASP.NET controls.
So obviously you get considerable productivity gains by using OutPost to AJAX-enable your web applications.
What makes OutPost unique?
OutPost is a new angle on AJAX. Other AJAX frameworks use specifically tailored Web Services that you have to write yourself to return the data you need. And none of the frameworks fully support the benefits of the ASP.NET Framework. With AJAX you have to re-think and re-write your code.
Unique advantages of OutPost
- A single .js include file is enough for OutPost.
- OutPost converts any ASP.NET WebForm into an AJAXed WebForm without any modifications to the code.
- It has cross-browser support built-in.
- The postbacks are 100% real and server-side events are triggered, as you'd expect them to trigger.
- Multiple server-side WebForms (.aspx-pages) are supported side by side on the same page.
- Excess traffic between client and server is eliminated (the view-state stays on the server and only the modified HTML is sent back to the client).
- A lightweight JavaScript client-framework makes it possible to add, remove and postback WebForms by script.
Other OutPost features
- Output caching is supported on WebForm-level.
- Unicode characters are supported.
- A WebForm has its own Session.
- No custom controls are needed.
- Main pages and WebForms can be designed in VS.NET.
OutPost-enabling a single WebForm
The decision to add "AJAX" to your pages is not a question of "do we have the time and skill to implement it". Instead it becomes a design question.
To add OutPost and AJAX-enable a WebForm, all you need is to write the following in the beginning of your .aspx file:
<script src="/OutPost/OutPost.js.aspx" type="text/javascript"></script>
But let us just zoom out for a second:
Background
When developing interactive applications for the web, you have different models to use, and two of them are shown below. They can be combined in many ways, but if they are not combined the following is a non-pre-emptive comparison of their advantages and disadvantages:
ASP.NET WebForms
+ | Fast and safe: Compiled code, type safe code model. |
+ | Open OOP model: Object oriented programming model, open extendable classes, reusable code. |
+ | Framework: Has an extensible framework with a fair amount of rapid development tools and web controls ready to use. |
+ | State managed: It manages the state for you using viewstate post backs. |
+ | Server-Events: An event model takes care of the communication between the client and the server. |
+ | Cross-Browser: The HTML and JavaScript it produces automatically match the capabilities of the target browser. |
+ | Cached output: Pages, controls and datasets can be cached in a variety of ways. |
+ | Fast startup: Only the HTML needed for displaying the current page is transmitted to the client. |
- | Heavy traffic: When posting back, the whole web form including the viewstate is uploaded to the server and the whole page is then sent back to the client. |
- | Reloads: When posting back, the whole page is refreshed, and especially on slow connections and or slow CPUs, this makes the page flicker. |
AJAX
+ | Light traffic: State is not transmitted back to the server, only the needed data is transmitted |
+ | No reloads: The page is updated by using DHTML (manipulating the DOM with JavaScript). |
- | No Server-Events: Communication with the server is done by directly calling different web service methods. |
- | Slow and unsafe: JScript is not a compiled language and testing is pretty difficult on some devices. |
- | Slow startup: Huge amounts of JavaScript is usually needed for client controls and these files are sent to the client on startup. |
- | Not cross browser: Making sure the DHTML is cross browser is completely in the hands of the developer. |
- | No framework: You have to put together your own client-control framework to manipulate the DOM. |
OutPost - The best of both worlds
In OutPost the advantages of ASP.NET WebForms and AJAX are combined. OutPost takes care of the following:
- Need for cross-browser DHTML is eliminated.
- A real postback is executed and server-side events are fired.
- Excess traffic is eliminated.
- Page reloads are eliminated.
That's easy, right? Yes, let's take it step by step.
Step by step
The steps below describe the life of a postback when using OutPost:
- Need for cross-browser DHTML is eliminated
- A main page loads the Web Form page and in that process (on the server) the HTML and ViewState are stored for later use (it is used again on the server when posting back the WebForm). Also the needed link(s) to JavaScript file(s) are inserted in the
HEAD
section of the HTML (extra cross-browser compatibility layer scripts are added if necessary).
When loading a WebForm, you can decide whether you want all the HTML or only the outerHTML
of the form. If you want several forms loaded onto the same main page, you would only need the outerHTML
of each form and add that, where you'd like on the page (as long as it is not inside the page's own web form).
If you load a single WebForm you could go for the HTML of the whole page and simply let that be the content of the main page. The main page does not necessarily need to have its own web form.
- The above is optional and you can insert the JavaScript link in your code yourself and use the
<outpost:script>
server-controls to add the cross-browser compatibility layers if needed.
The compatibility layers are added by the files AtlasCompat.js and AtlasCompat2.js from Atlas (the Microsoft ASP.NET 2.0 AJAX platform) and they ensure that the OutPost runtime works the same in all browsers. For example, on the window
and the document
elements, the attachEvent
and detachEvent
functions are added, so the DHTML in the OutPost runtime can be the same.
If you don't use a main page, the HTML structure and the ViewState of the first call will not be stored on the server and the first post back will have to send the ViewState to the server and the inner HTML of the whole web form will also be sent back to the client on the first postback.
- A real postback is executed and server-side events are fired
The postback of the form is replaced by out-of-band calls to a .NET Web Service. The calls are made from JavaScript without reloading the page. This is done by posting a SOAP-envelope to the Web Service using XmlHttp. The SOAP-envelope contains the URI of the WebForm page (the action
attribute of the form
element) and the contents of the input fields in the WebForm (but not the ViewState).
The Web Service then executes the real postback using HttpWebRequest
and HttpWebResponse
. Headers containing user agent, language and session cookie are sent on to the WebForm page.
- Excess traffic is eliminated
The HTML read from the HttpWebResponse
is parsed and the tags/elements that have an ID gets picked out and put into a tree structure. This step only works if the HTML structure is correct. If the structure is not correct the inner HTML of the whole web form is sent back to the client (not the ViewState though).
The HTML tree structure of the new postback is compared to the HTML tree structure of the last postback. This is done in a bottom-up way, so first the contents of the form without any inner HTML of sub nodes are compared. If no differences are found, the inner HTML of the next sub nodes (with the same ID and ID-index) from each tree are compared without the inner HTML of their sub nodes. And so forth.
- Page reloads are eliminated
The inner HTML of the elements found to be different in the previous step are returned to the client. The DOM is updated, element by element, using cross browser DHTML.
It might seem as a bit of a crude way to just send the modified innerHTML
fragments back to the client, but it really makes sense when developing cross-browser web applications.
Using the code
A single WebForm
To add OutPost and AJAX-enable a WebForm, all you need is to write the following in the beginning of your .aspx file:
<script src="/OutPost/OutPost.js.aspx" type="text/javascript"></script>
Using a main page to load the WebForm
To load a page from a main page and OutPost-enable it, you write the following in your Page_Load
method:
private void Page_Load(object sender, System.EventArgs e) {
Page.Controls.Add(OutPost.Core.OutPost.GetLiteralControl(
ResolveUrl("Validator7.aspx")));
}
This (when placed in the top) adds cross-browser scripts on demand, based on the client User-Agent:
<%@ Register TagPrefix="outpost"
Namespace="OutPost.Controls" Assembly="OutPost" %>
<outpost:script Runat="server" Path="~/ScriptLibrary/AtlasCompat.js"
Browsers="Netscape,Mozilla,Firefox,Apple,Safari,Mac" />
<outpost:script Runat="server" Path="~/ScriptLibrary/AtlasCompat2.js"
Browsers="Apple,Safari,Mac" />
Having two forms on the same page that depend on each other
Using JavaScript, it is possible to let two forms depend on each other:
<script type="text/javascript">
function document_onkeyup() {
if (event.srcElement==Forms[0].TextBox1) {
Forms[1].TextBox1.value = event.srcElement.value;
}
}
function document_onclick() {
if (event.srcElement==Forms[0].Button1) {
Forms[1].Button1.bClicked = true;
Forms[1].doPostBack('','');
}
}
document.attachEvent("onclick",document_onclick);
document.attachEvent("onkeyup",document_onkeyup);
</script>
Using the Client Framework
The following example shows how you can dynamically add, remove and postback different forms on a page (see "The iGoogle Demo" in the demo project and the screenshot from the demo in the beginning of this article):
function DeleteBox() {
var oNodeForm = null;
if (oEventNode) {
oNodeForm = misc_getForm(oEventNode);
oEventNode = null;
}
if (oNodeForm!=null) {
oNodeForm.parentNode.parentNode.removeChild(oNodeForm.parentNode);
Forms[oNodeForm.iIndex] = null;
}
var sAction = unescape(oNodeForm.action);
FormMain.bAddBox = false;
FormMain.doPostBack("oButtonDelete",sAction);
}
function AddBox() {
var sUri = "/OutPost/Demo/RssFeed.aspx?rss="+
escape(document.getElementById('FormMain').oTextBoxAddRssUri.value);
for (var i in Forms) {
if ((Forms[i]!=null) && (unescape(Forms[i].action)==unescape(sUri))) {
alert("The feed has already been added");
return;
}
}
var oNodePanel = document.createElement("DIV");
oNodePanel.className = "cPage";
var oNodeColumn = document.getElementById("oTableCell2");
if (document.getElementById("oTableCell1").childNodes.length
<oNodeColumn.childNodes.length) {
oNodeColumn = document.getElementById("oTableCell1");
}
if (document.getElementById("oTableCell3").childNodes.length
<oNodeColumn.childNodes.length) {
oNodeColumn = document.getElementById("oTableCell3");
}
oNodeColumn.appendChild(oNodePanel);
var oNodeForm = createForm("Form1", sUri, AddBox_onerror);
oNodePanel.appendChild(oNodeForm);
document.getElementById('FormMain').oTextBoxAddRssUri.value = "";
}
function AddBox_onerror() {
alert("Error adding RSS feed");
}
function FormMain_onsuccess() {
this.base_onsuccess();
if (this.bAddBox!=false) {
AddBox();
}
this.bAddBox = true;
}
function window_onload() {
FormMain.onsuccess = FormMain_onsuccess;
}
window.attachEvent("onload", window_onload);
The Future of OutPost
- Making it possible to send back changes on a smaller scale (like only setting the value of an input or only changing one option in a dropdown list).
- I would like to incorporate simple AJAX behaviour, so that you can interact with business logic on the web server.
- Solving the Bookmark issue. It is not possible to bookmark a WebForm page and I'm trying to find a solution to that.
- Solving the Back-button issue. It is not possible to use Back-button and I'm trying to find a solution to that also.
History
- Sep 2005 - OutPost version 1.0 - Added cross-browser support, lightweight client-framework.
- May 2005 - OutPost version 0.5 - Single web form supported, IE only.
- Nov 2004 - First initial testing performed.
Updates
- 4/10-2006 - 1.5.003
- Added more thorough removal of EventValidation and ViewState input including surrounding
DIV
s
- Removed bits of JavaScript that wasn't used for anything
- 1/10-2006 - 1.5.002
- Removed selected="selected" when comparing HTML, because it caused dropdown boxes to be refreshed without any need to
- Removed call to
oForm_doReset
after each post back because of the change to the dropdowns
- Added update of SignUp demo - it now has three "linked" controls (two drop-downs and a text field)
- 1/10-2006 - 1.5.001
- Added gzip and deflate compression internally
- Changed namespace
OutPost.Core
to OutPost
- Removed constant
ContentTypeForNoCompression
- 28/9-2006 - 1.4.004
- Added error message when trying to view OutPost.aspx directly
- Added
MinimumSizeForCompression
and ContentTypeForNoCompression
constants
- 28/9-2006 - 1.4.003
- Changed "SessionID" and "session" to "GUID" and "guid"
- Optimized client framework size by removing spaces and replacing variable names
- 27/9-2006 - 1.4.002
- Added default setting of
PartsNeeded
for GET request
- Changed "SessionID" and "session" to "GUID" and "guid"
- 26/9-2006 - 1.4.001
- Replaced OutPost.asmx with OutPost.aspx and OutPost.js.aspx with OutPost.aspx/Js
- Optimized client framework size
- 25/9-2006 - 1.3.002
- Optimized the POST size
- Optimized the body size of request and response
- Got rid of SOAP envelopes
- 21/9-2006 - 1.3.001
- Found problems with Unicode characters. As it is, your page must include the tag ResponseEncoding="utf-8" in order to work properly with Unicode characters
- Added a cloak (event trap) over the form in IE on postback in order to mimic the default web form behaviour
- Added support for
WebForm_AutoFocus
- Added support for queuing requests
- Added support for reloading script blocks
- Added setting
LocalHost
to specify alternative localhost domain
- Added support for HTTP-compression (tested with FlatCompression R1.20.226)
- 17/3-2006 - 1.2.002
- Removed Session dependencies
- Restores position and selection in IE on postback
- 16/11-2005 - 1.2.001
- Added Session support on first post back
- Added filter to take advantage of differences on first post back
- 16/11-2005 - 1.1.002
- Added more CookieContainers
- Merging ASP1.1 and ASP2.0 core files
- 18/10-2005 - 1.1.001
- Paths to OutPost.asmx and OutPost.js.aspx are read from web.config
- Support for Netscape
- Support for Opera
- 14/10-2005 - 1.1.000
- Support for HTTPS/SSL
- Support for Visual Studio 2005 Release Candidate
- 7/10-2005 - 1.0.002
- Bug fixed in JavaScript Serializer: Unicode characters were not escaped properly
- Optimization of the JavaScript property by using
StringBuilder
- Default values are stored before PostBack and restored after PostBack
- 4/10-2005 - 1.0.001
- Bug fixed in constructor:
PartsNeeded
is only initialized as a StringArray
if the "PartsNeeded
" header is set
- Bug fixed in
window_onload
function: Script error if no forms present on page