This post is meant so I could write some notes for myself while reading AJAX In Action (Manning, 2007). Feel free to read my comments, but do remember that the target audience is one well-armed MS AJAX self-proclaimed guru.
Page 41 - Javascript diagram
I don't like this diagram. It's very demagogic. It shows a very organized "Server" and a very messy "Client".
Also, It doesn't highlight the fact the Javascript isn't just one more thing in the browser, It is AJAX.
Javascript isn't equivalent to DOM0, DOM2, CSS & XmlHttpRequest object.
It's bigger then any one technology, it's the glue that binds everything together.
Page 48 - That's the point of all this AJAX stuff
The book features this little XmlHttpRequest code (after properly explaining all the different members & cross-browser initialization issues).
OK, pretty scary and I'm having flash-backs just by looking at "readyState == 4" and "status == 200".
However, then the authors just keep going like nothing happened and explain about AJAX.
People, Microsoft AJAX exists for one reason - developers are lazy and don't want to write code. Period.
We've just seen we can update a <div> text without MS AJAX.
This would have been a perfect place to show the 2 (!) lines of code needed by MS AJAX to do the exact same thing.
Page 52 - Xml-Script
Ok, Seriously, didn't the authors get the memo? Xml-script is dead.
Dead.
We took it out back and shoot it like the old diseased dog it was.
So why is the first MS AJAX sample in this entire book of Xml-Script?
Page 60 - We finally got started!
Finally, 60 pages and now they remember to open a new project!
Woo hoo!
Everywhere - Cross-browser under the radar
This one is just cool. Without telling the reader to pay attention - the authors randomly demo MS AJAX in different browsers.
Page 67 - Showing the Javascript proxy
This means nothing to everyone.
It's a bunch of random Javascript code.
If you're going to show it - explain what it does.
What I did in my "Will the Real Ajax presentation" (given at Jan '07 in front of 1,000+ folks) was the following slide:
After drawing the watcher's attention to the relevent piece of code, I explained that this is the code that gives us "PageMethods" class of type "Sys.Net.WebServiceProxy" with a method "PageMethods.HasBlog". No point in showing code unless you explain it's highlights.
Page 68 - Using the MS AJAX $AddHandler and $get for event handling
Very nice syntax.
I don't use it often, I just use the normal onchange="depertment_onchange" HTML DOM0 events registration.
That's the common way of doing that... But It's still a nice syntax.
Page 77 - ASP.Net Page == MS AJAX Application
Hmm... I never thought about this like that. That's a very true association.
I always thought of "Sys.Application" (Javascript) as the equivalent of "ApplicationDomain.CurrentDomain" (.Net).
It made since because the MS AJAX domain starts when browser loads a page and ends when it exits the page.
But it makes more sense to say that "Sys.Application" since also knows of events in the current MS Ajax page that it's equivalent to "System.Web.UI.Page".
Page 81 - "pageLoad" and "pageUnload" are automaticlly called
<asp:ScriptManager ID="TheScriptManager" runat="server"></asp:ScriptManager>
<script type="text/javascript">
Sys.Application.add_init(pageInit);
function pageInit() {
alert("Entered the Init stage!");
}
function pageLoad() {
alert("Page loaded!");
alert("Hello Microsoft Ajax!");
}
function pageUnload() {
alert("Unloading page!");
}
</script>
These two "pageLoad" and "pageUnload" methods are called without being attached to any event!
It's a "string based" function search and if the function exists then the MS AJAX framework fires it.
Yep, I Just learned something new.
Have a look at the code inside the framework that fires the "load" event and after that actually looks a for a method called "pageLoad" to lunch.
function Sys$_Application$raiseLoad() {
var h = this.get_events().getHandler("load");
var args = new Sys.ApplicationLoadEventArgs(Array.clone(this._createdComponents), !this._initializing);
if (h) {
h(this, args);
}
if (window.pageLoad) {
window.pageLoad(this, args);
}
this._createdComponents = [];
}
Page 87 - Dom abstraction
Have a look at the documentation for these two classes:
From http://asp.net/ajax/documentation/live/Samples/Sys.UI.DomEvent/cs/Default.aspx:
I really like the "preventDefault()" method and the "stopPropagation()" since they give us the option of fine-grain event firing control.
from http://www.asp.net/ajax/documentation/live/Samples/Sys.UI.DomElement/cs/Default.aspx:
I've worked with both this classes for a while now, they're very useful.
Page 100 - Browser detection
*insert evil laughter*
Opera... 9.1... MS AJAX... :)
Page 102 - Debugging
The authors show this code:
And how it looks like in FireBug:
I was checking if I could make the Internet Explorer developer toolbar work with this, but it doesn't look like it can.
If we want to see the debugging information we have to add this HTML code:
I checked inside the MicrosoftAjax.js framework and again, there's a hard-coded ID and tag name.
function Sys$_Debug$_appendTrace(text) {
var traceElement = document.getElementById('TraceConsole');
if (traceElement && (traceElement.tagName.toUpperCase() === 'TEXTAREA')) {
traceElement.value += text + '\n';
}
}
It looks like the MS AJAX framework has a lot of "Magic strings" going around.
Just goes to show you, After two years of working with this framework (Since Atlas December '05 CTP) this is the first time I see this hard-coded values. Imagine if they were Global variables that we could set on "page_init" event, it would have actually been useful.
Page 165 - Having a LOL moment
Page 174 - Changing the Error message at Server-side
protected void Page_Load(object sender, EventArgs e)
{
ScriptManager scriptManager = ScriptManager.GetCurrent(this.Page);
scriptManager.AsyncPostBackError += new EventHandler<AsyncPostBackErrorEventArgs>(OnAsyncPostBackError);
}
void OnAsyncPostBackError(object sender, AsyncPostBackErrorEventArgs e)
{
ScriptManager.GetCurrent(this.Page).AsyncPostBackErrorMessage = "We're sorry, an unexpected error has occurred.";
}
Nice.
But since client-side errors still occur, you will still have to implement a client-side global page request error handling policy.
From the PageRequestManager.EndRequest event:
http://www.asp.net/AJAX/Documentation/Live/ClientReference/Sys.WebForms/
PageRequestManagerClass/PageRequestManagerEndRequestEvent.aspx
<script type="text/javascript" language="javascript">
Sys.WebForms.PageRequestManager.getInstance().add_endRequest(EndRequestHandler);
function EndRequestHandler(sender, args)
{
if (args.get_error() != undefined)
{
var errorMessage = args.get_error().message;
args.set_errorHandled(true);
ToggleAlertDiv('visible');
$get(messageElem).innerHTML = errorMessage;
}
}
</script>
BTW, I really don't like this repeating pattern in the book to keep using the ScriptManager.GetCurrent everywhere.
This should be encapsulated in some base class.
Before:
public partial class WebForm2 : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
ScriptManager scriptManager = ScriptManager.GetCurrent(this.Page);
scriptManager.AsyncPostBackError += new EventHandler<AsyncPostBackErrorEventArgs>(OnAsyncPostBackError);
}
void OnAsyncPostBackError(object sender, AsyncPostBackErrorEventArgs e)
{
ScriptManager.GetCurrent(this.Page).AsyncPostBackErrorMessage = "We're sorry, an unexpected error has occurred.";
}
}
After:
public abstract class BasePage : Page
{
public ScriptManager CurrentScriptManager
{
get
{
return System.Web.UI.ScriptManager.GetCurrent(this);
}
}
}
public partial class WebForm2 : BasePage
{
protected void Page_Load(object sender, EventArgs e)
{
CurrentScriptManager.AsyncPostBackError += new EventHandler<AsyncPostBackErrorEventArgs>(OnAsyncPostBackError);
}
void OnAsyncPostBackError(object sender, AsyncPostBackErrorEventArgs e)
{
CurrentScriptManager.AsyncPostBackErrorMessage = "We're sorry, an unexpected error has occurred.";
}
}
No point in invoking the same function 5 times in 10 rows and not making sure the call isn't duplicated for nothing.
Page 185 - Javascript webservices call timeouts
// Javascript
I'd have to disagree on this one.
There's no point in having two different timeouts: one for the first request and another for the other.
Let's say we have two requests: R1 & R2.
Based on that tip, R1 will have a timeout of 2000MS (2seconds) and R2 will have a timeout of 7000MS (7 seconds).
So R1 times out and R2 returns successfully after 3 seconds.
Why not just give R2 7 seconds to start with?
There's no point in having a first request with a short timeout and second one with a longer one.
It just doesn't give you advantage. If anything, Calls will take longer because some calls will fail on the first attempt (R1) and will succeed only on the second (R2).
However, I do agree with the authors that if R1 times out you should try again.
Double Ajax request over the web (and maybe accessing old-data from a local cache) is a MUST in Ajax development today.
Page 196 - Manual AJAX Requests
The Sys.Net.WebRequest Javascript class is a very important part of the Microsoft AJAX communication layer framework.
Let's say we don't like how Webservice calls work right now, or it doesn't give us enough cross-browser compatibility.
We can write our own AJAX communication framework based on Microsoft AJAX.
Let's look at the class members:
The minimu to set is the url property, the oncompleted event and start the Request.
But we can also set the httpVerb (POST/GET), request text body and request headers.
The really interesting part though is the "executor" property.
By default we use the Sys.Net.XmlHttpExecutor class which as the name say: executes request using a XmlHttpRequest transport.
We can write our own executor for browsers which aren't supported by Microsoft AJAX.
Or even write an Executor which enables cross-domain requests using the <script src> browser hack.
This level of abstraction inside the Microsoft AJAX frameworks enables us to develop our own AJAX framework based on Microsoft AJAX.
BTW, do the members of the Sys.Net.XmlHttpExecutor class look familiar?
Yep, these are the properties for the browser XmlHttpRequest.
So, if a Microsoft AJAX request fails, we can access the Executor and see the raw XmlHttpRequest data:
Page 206 - UpdatePanel render mode
Apparently, Updatepanel can be set using the "RenderMode=Inline" to render out a <span> tag.
<asp:UpdatePanel ID="UpdatePanel1" runat="server" RenderMode="Inline">
<ContentTemplate>
<%= DateTime.Now.ToLongTimeString() %>
</ContentTemplate>
</asp:UpdatePanel>
Renders to:
<span id="Span1">
18:56:12
</span>
While the default of RenderMode="Block" renders to a <div> tag.
<div id="Div1">
18:56:12
</div>
I wonder why does the normal <asp:Panel> doesn't have that... (an <asp:Label> which renders to a <span> tag doesn't doesn't render out any children Controls so it's not the same as an <asp:Panel> tag)
Page 270 - The dangers of UpdatePanel
So, this was pretty inspirational.
UpdatePanel is like an arsenal of tools, gotcha.
You know what else the Swiss have? A mandatory 15 months military draft.
After which they all go home with their personal M16.
If we're starting to talk about "the advanced features of an UpdatePanel" wouldn't this be the time to mention that it should be used only sparsely? That at times it's not a Swiss army knife, but a Swiss army M16? And you might be shooting yourself in the leg with it?
Page 272 - Manually updating UpdatePanels with Javascript
Yes, the "_updateControls" Javascript function can cause UpdatePanels to perform a partial postback.
It CAN, but SHOULD we use it?
There's no clear answer to this question.
On the one hand, It is the fastest way of getting UpdatePanel to refresh from Javascript and it does work.
One the other hand, It's invoking a private Javascript function that's not meant for public use. So it's basically a hack.
If it's possible, we should avoid calling private javascript functions.
So how do we force an UpdatePanel to refresh from Javascript "in a good way"?
The easiest thing to do would be to add an <asp:button> to the page, make it an AsyncPostBackTrigger and have Javascript trigger it's click event.
If you're up to something a bit more complex, you can build your own AsyncPostBackTrigger that exposes a Javascript function you'll invoke.
Either way, I strong heartedly recommend working "within" the Microsoft Ajax paradigm and not outside of it by exploiting hacks.
Page 287 - Having a LOL moment
Page 307 - The $create statement equals
Is in fact
When I saw the first line, I really did think to myself "why don't they use the Javascript new operator?". The authors actually answered this question on the next page. This really goes to show you the authors of this book are great instructors. The good ones teach something the way they understand it. The great ones, teach something the way the student thinks about it.
Page 338 - Generating $create statements from ASP.Net server controls
I like Script descriptors since they enable us to stop emitting "$create" statements from ASP.Net server controls.
However, I don't like the amount of code that's needed for that.
First, have a look at this example of Javascript "$create" statement.
This can be generated from an ASP.Net server controls by using the following ScriptDescriptor:
OK, That's quite a lot to write and it's mostly loosely typed. It's a good syntax, not great, but it's OK.
My problem is the WHERE this code is written and the amount of code needed to inject it.
public class AjaxLogin : Login, IScriptControl
{
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
ScriptManager manager = ScriptManager.GetCurrent(this.Page);
if (manager == null)
throw new InvalidOperationException("A ScriptManager is required on the page.");
manager.RegisterScriptControl(this);
}
protected override void Render(HtmlTextWriter writer)
{
base.Render(writer);
ScriptManager.GetCurrent(this.Page).RegisterScriptDescriptors(this);
}
public IEnumerable<ScriptDescriptor> GetScriptDescriptors()
{
ScriptControlDescriptor descriptor = new ScriptControlDescriptor("Samples.AjaxLogin", this.ClientID);
AddControlIDToScript(descriptor, "UserName");
AddControlIDToScript(descriptor, "Password");
AddControlIDToScript(descriptor, "RememberMe");
AddControlIDToScript(descriptor, "LoginButton");
yield return descriptor;
}
public IEnumerable<ScriptReference> GetScriptReferences()
{
yield return new ScriptReference(Page.ClientScript.GetWebResourceUrl(typeof(AjaxLogin),
"ScriptControls.AjaxLogin.js"));
}
}
First, we implement the "IScriptControl" interface.
Second, we need to implement both it's method - Where to get the Controls JS files and how to initialize them.
Third, we have to inject ourselves both in the Control's PreRender & Render methods. That's something I just don't like. I would have encupsulated this logic in a base control class.
But we can't always do that, have a look from which class are we inheriting from. And we can't even sign up for events from the outside since we have to inject directly into the Render method.
I'm just not that sure this ScriptDescriptor syntax actually saves us from one form of repeatable coding to another.
The Extender Syntax does make a lot more sense though:
[TargetControlType(typeof(TextBox))]
public class FormattingExtender : ExtenderControl
{
public string HoverCssClass
{
get { return (string)ViewState["HoverCssClass"]; }
set { ViewState["HoverCssClass"] = value; }
}
public string FocusCssClass
{
get { return (string)ViewState["FocusCssClass"]; }
set { ViewState["FocusCssClass"] = value; }
}
public string ScriptPath
{
get { return (string)ViewState["ScriptPath"]; }
set { ViewState["ScriptPath"] = value; }
}
protected override IEnumerable<ScriptDescriptor> GetScriptDescriptors(Control targetControl)
{
ScriptBehaviorDescriptor desc = new ScriptBehaviorDescriptor("Samples.FormattingBehavior",
targetControl.ClientID);
desc.AddProperty("hoverCssClass", this.HoverCssClass);
desc.AddProperty("focusCssClass", this.FocusCssClass);
yield return desc;
}
protected override IEnumerable<ScriptReference> GetScriptReferences()
{
yield return new ScriptReference(Page.ResolveClientUrl(this.ScriptPath));
}
}
No "PreRender" or "Render" injection does make this more readable.
In page 357 the authors once more read my mind and show this table of when to use each tactic.
Just note that the extender option is missing. We should always consider building a MS Ajax extender control when we want reusability of "ScriptDescriptors" & "ScriptReferences" between different controls.
Chatper 10 - Microsoft Ajax Control toolkit
I often develop custom Microsoft AJAX Control Toolkit classes and debug the existing one.
So, I'm pretty familiar with the development modal for MS Ajax control toolkit.
This chapter is a must-read for anyone who needs to understand the MS AJAX control toolkit.
This chapter is the single best-source on MS Control toolkit development. I had to learn the internal development modal based on reading thousands of line of code. This chapter gives the reader a very good foothold on what's going on inside the toolkit without spending count-less hours reading tons of code, unclear blogs posts and semi-coherent internal documentation.
The authors really did a good job on this one. (Only if by being the only ones writing on the topic)
For instance, just this attributes table that's used inside the Toolkit goes a long way into helping a novice toolkit developer.

One more thing that can found at this chapter is a basic explaination on the Microsoft Ajax Control toolkit Animation framework. Let me tell you, I had to read a 200KB Javascript file just to learn this stuff. It's a very good improvement there's finally a good reference for this.
There's a good example of how the animation framework works in the book.
Say we have a Button and a Panel controls on a page.
<asp:ScriptManager ID="TheScriptManager" runat="server"></asp:ScriptManager>
<div id="thePanel" style="background-color:#aaa">
<h2>Click the button to dismiss me.</h2>
</div>
<asp:Button ID="Button1" runat="server" Text="Click Me" OnClientClick="return false" UseSubmitBehavior="false" />
We want that the button will zoom in 200% when the page loads. When the button is clicked we want the Panel to fadeout elegantly.
<ajaxToolkit:AnimationExtender ID="AnimationExtender1" runat="server"
TargetControlID="Button1">
<Animations>
<OnLoad>
<Scale ScaleFactor="2" />
</OnLoad>
<OnClick>
<Sequence>
<EnableAction Enabled="false" />
<FadeOut AnimationTarget="thePanel"
MinimumOpacity="0"
MaximumOpacity="1"
/>
</Sequence>
</OnClick>
</Animations>
</ajaxToolkit:AnimationExtender>
Print screen of the page load a 0 seconds:
Print screen of the page load after 2/3 seconds: (when the button scales out)
And after the button is clicked the headline fades out nicely:
1.
2.
This really reminds me of how Silverlight/WPF animations work. Though it's a bit more Effects oriented then animations oriented.
The really cool thing about all of this is that's it's a quickstart into the animation framework AFTER the book has taught you all the server-side and client-side knowledge you need.
Just by showing this table they save the developer reading the Javascript file that declares this classes:
Page 481 - Having Javascript Call-stack when debugging
The authors show the following javascript code:
And then how it looks like in the VS2005 Javascript call-stack debugger:
They recommend using the following pattern to enable Call-stack javascript function naming:
Which leads to:
I have to disagree with this way of authoring Javascript files for two reasons:
1. code duplication "person.set_name = person$set$name" is the most obvious bit of text duplication. Overtime we rename something we have to rename is twice. I'm willing to not have Javascript call-stack function names just because of that.
2. The specific syntax the authors use doesn't enable in-class javascript intellisense when working in VS2008. Since the methods are only entered into the class after all methods are written using the "this." operator would have no meaning.
My sum up
Good book, 4 stars.
Buy it if you can spend 2 days learning level 400 of the Microsoft Ajax framework.
Why only 4 stars?
1. The authors tend to talk about trivial topics when there're bigger issues to cover. A good example of when they actually "talked about" each line of code inside the UpdatePanel update process. Nice, but completely useless. If someone needs to know that, he can just read the code himself.
Additionally, There're 2.5 chapters in the book dedicated to the old May 07 AJAX futures release. Mainly about Xml-script and Drag&Drop support. While each of them is "a technology" I'm not sure they're more then a passing note in the history of Microsoft Ajax.
2. There's something missing when reading this book. It's never quite 100% clear why are we doing what we're doing. It's clear WHAT we're doing, but not WHY. What's the end-goal of this Javascript syntax? of this Ajax enables control?
There's a "big picture to little picture" zoom-in missing.
There's a lot of tech stuff in this book, but how it all comes together is sorely missing.
3. I personally have to disagree with the way the book is structured. The chapters aren't arranged to what I'd consider a logical order. We're jumping back & forth from Javascript to C# and IMHO there're better ways of showing some of the concepts in this book. There's also a lot of confusion in the first chapters about what are the authors trying to show. This is just a passing comment since this once is VERY subjective.
Why 4 stars? This book took the seemingly simple task upon himself to teach "Microsoft Ajax".
Which is what? 200KB Javascript files and 700KB .Net DLL file. Not much.
But teaching Microsoft Ajax is really teaching how the Server-side is incorporated into the Client-side and how we make Client-side programming more like Server-side programming.
That's a very big mission - Teaching C# developers how to develop Javascript Object-oriented AJAX code.
An it's an objective this book does very very well. Even if you don't have any Javascript background except the occasion JS function, it even teaches you the basics of Javascript OO.
The good - This book will school you to a good level in Microsoft Ajax, Microsoft Ajax Control toolkit and General Javascript development. If you'll read it you'll be in a great shape. Great, but you won't end up a guru.
The bad - You need patience for this book and that means time. For the amount of time a normal (read: "Non-justin") reader needs for this book, I would expect a bit more. So lower your executions a bit when approaching this book.