AjaxWorld Talk: Building a Great Ajax application from Scratch
I had a great time today at AjaxWorld during my talk on building a great Ajax Application from Scratch. It was fun to get out from behind the powerpoint and show some real code!
For this demo, I only used freely available tools... In particular everything I show works in Visual Studio 2008 Web Developer Express.
Peter Galli posts on Microsoft's general presence at AjaxWorld.
The folks at sys-con already posted a quick story on my talk:
Presented A No-Slides, Code Only Session at AJAXWorld
Download the complete samples.
Part I: CSS and HTML Designer
First, I showed off what a great CSS and HTML designer Visual Studio 2008 is. For this part of the demo I didn't use any ASP.NET or .NET specific code... All of this works great with plain old HTML and CSS...
Download and Install Visual Web Developer Express (free)
File\New Project and select the ASP.NET Web Application
Then I dragged in some HTML to start with, and a directory of Image..
I showed off VS's split view... how you can type or highlight in the design view and have it show in the code view (and vice-versa).. This is an excellent feature for tracking down what markup corresponds what visual element.
OK - now what we really want is a nice grid-like display rather than the single list. To do this, let's use standards based CSS that works cleanly in any browser.
I'll use VS 2008's great new CSS support. I used the Manage Styles panel to create a new CSS Selector... Notice I use ".photolist li" this rule will apply to any li inside an element with the class photolist.
Also, notice we can select where to define the css rules in. I have selected in a new style sheet. VS will create the new style sheet and (optionally) reference it from the current page.
In this case, I create a new style sheet and VS automatically add a reference to it from my HTML document.
Then notice, back in the HTML editor I get great intellisense..
Now, I can open up the CSS file and see how clean it is? Also, notice I can do code-focused editing of the CSS rather than use the designers. Again, I get great help from VS.
Next, in design view, I click on an image... notice the breadcrumb at the bottom of VS shows you where you are in the visual tree. Hit escape to navigate up the tree until the list item (li) is selected.
We want this to layout like a grid, so in the CSS Properties window, under Layout, set Display to "inline" and float to "left"
That looks pretty good, but we need a bit more padding, so let's use the grab-handles in the designer to give us a little more spacing...
Again, notice how all these edits are cleanly going into the attached CSS style sheet rather than gunking up our code.
Finally, let's make this look really nice by throwing it a MasterPage will will give a common look and feel across our site. We will do this by dragging in Site.master and Site.Master.cs.
And we are starting go have something that looks sort of nice!
Part II: Data access with Linq
OK, that was great, but in a real world site you don't deal with static images... you need to pull data out of a database. So let's change the site around a bit to pull data from a database.
First, I will drag in photos.mdb into App_Data, this is a SqlServer database that I created for this demo... I will use the free Sql Express to allow me to use it at development time.
As you can see it is a simple database.
Now, as a .NET developer, I'd really like to operate against this database as a set of .NET types rather than having to remember and debug some T-Sql code in my .NET code. Luckily Linq solves this for me!
To add a Linq data model for this database... Add New Item, then select ADO.NET Entity Data Model
We select to work from a database, then the Entity Framework designer knows we have a Sql Express database in the project and defaults to using it. Notice EF support TONs of different databases including MySql and Orcale!
Then we select what tables we want to include in our model.
Then you see the design surface showing you what the designer created.
This is the view of your .NET types that represent your database. Notice VS picked up on the relationships between the tables. You can of course edit these names in this designer view as well.
Notice, while this will do direct CRUD operations against the database, it is very possible to encapsulate all access through stored procs if need be.
Now, let's go back to the UI and give us some place to put this data. I will show off the new ListView control in ASP.NET.
The way ListView works, is that you give it a layout template that defines exactly how you work the markup to look. In our case, we will just cut and pate that ul into the layout template and define a place holder of the li's
Now we just need to get some real data.
1: protected void Page_Load(object sender, EventArgs e)
2: {
3: int rating = 2;
4: var db = new PhotosEntities();
5: var q = from photo in db.PhotoSet
6: where photo.Rating > rating
7: orderby photo.Tags.FirstOrDefault().TagName descending
8: select photo;
9: this.lv.DataSource = q;
10: lv.DataBind();
11: }
In line 4, we create the data context... this is the logic connection string for our Linq data model.
In line 5, we are using C#'s new var keyword which allows us to fudge on the type of the express...the compiler will figure it out...
In line 6, we are doing a where clause, notice that I am capturing a local variable in that, clearly this example is trivial, but it is a powerful feature.
in line 7 notice i am ordering by the first tag name, notice how nice this reads? It would be much harder to do this in TSQL!
in line 9 and 10 and I doing the normal databinding.
Now we are really starting to look good!
Now, that is nice, but there are a ton of results here.. we really need a nice way to page through them.
I use the new DataPager control in ASP.NET 3.5.. Notice it can have filed that are simple Next\Prev or numeric or you can even build your own!
Here is the complete code:
1: <asp:DataPager runat="server" ID="dp"
2: PagedControlID="lv">
3: <Fields>
4: <asp:NumericPagerField />
5: </Fields>
6: <asp:DataPager>
Now, we need to ensure that the ListView will show the correct items when the data pager changes. We do this by adding a PreRender() method to the page. A simplified view of the page lifecycle is Page_Load(), then the control events are raised, then Page_PreRender() is called. In our case the events from the pager need to be raised to change the page before we bind the list.
protected void Page_PreRender() {
lv.DataBind();
}
And notice it works great!
Part III: Server Side Ajax
Now it is time to add a little ajax fun to the site. Notice as I page through the items I am getting a little blink? That is because the page is doing a full post-back to the server when only the images are changing. It would be great if I could update only the part of the page that is changing!
And you can with UpdatePanel. Simply wrap the UpdatePanel's content template around the area of the page that you want to update separately. the runtime will turn any postbacks into XmlHttp calls, back the the server, get the right HTML and then bring in back and update only the part of the DOM effected. Simple. Powerful.
Oh, and don't forget to add ScriptManager to the top of the page. This "turns on" the ajax functionality in ASP.NET...
Now the page refreshes much more smoothly.
For the demo, we are running against localhost, but in the real world there is network load, congestion on the Internet, etc that may effect your page's responsiveness. But you want to give user's an immediate response to their actions... to let them know that something is happening.
You can use a Thread.Sleep (2000) in your page's load method to simulate this sort of load..
Enter UpdateProgress... This little control listens for calls over XmlHttp and shows a progress template as they go out and takes it down when they come back in... That plus a little animate gif gives users a great sense of "something happening".
<asp:UpdateProgress ID="UpdateProgress2" runat="server">
<ProgressTemplate>
<div id="UpdateProgressBox">
<img id="Img2" runat="server" alt="Loading" src="~/Images/ajax-loader.gif" /></div>
</ProgressTemplate>
</asp:UpdateProgress>
And now the site looks good!
Part IV: Client Side Ajax
Next we want to allow users to edit those titles, but in a smooth way that doesn't require form postback to the server.
We start by changing the rendering of the title to an input control
<input id="titleTextBox"
onchange="onTitleChange(this.value, <%#Eval("ID") %>);"
value="<%#Eval("Title") %>" />
Now we need to go define the onTitleChange client side javascript method.
We do this in the header content area our MasterPage has left us.
<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="server">
<script type="text/javascript"> 1:
2:
3: function onTitleChange(title, id) {
4:
5: }
6:
</script>
</asp:Content>
Notice that VS 2008 has some great intellisense for JavaScript. We are actually running a JavaScript interpreter in the background, so we always know what type the objects are.
For example, here VS knows n is a Dom element.
But we can easily change the type to a string... and VS keeps up!
We know debugging in JavaScript can be hard... I know I have used alert() based debugging too many times! Well, VS2008 is here to help with full debugging support for client side javascript.
Lots of cool stuff to show you here. Notice at the top right, full debugging controls, run, break, step over, step into, etc.
At the mid-left, notice the icon's for break point, and current executing line.
At the mid-right, notice the debugging views I get that show me exactly what the current value of all the locals and parameters are.
At the bottom right, notice my the results of my call to Sys.Debug.Trace() are beging displayed in the output window.
And finally at the bottom right, notice I have full control to use the immediate window to evaluate results in the current context.
Ok, cool, but now we want to actually go change the datamodel on the server... How do I do that?
Well, first define a WCF-Ajax service. This well expose JSON calls that is very ajax friendly. Add, New Item, Ajax-enabled WCF service.
Now, we need to implement a web services that will update our data model.
function onTitleChange(title, id) {
Sys.Debug.trace(title + id);
PhotoService.SetPhotoTitle(title, id, onComplete);
}
function onComplete(results) {
Sys.Debug.trace(results);
}
Then we just need to call it from client side javascript. To do that, we need to add a reference in ScriptManager.
<asp:ScriptManager runat="server" ID="sm">
<Services>
<asp:ServiceReference Path="~/PhotoService.svc" />
</Services>
</asp:ScriptManager>
Then, we can directly access the SetPhotoTitle method from JavaScript. Notice we even get auto-completion.
Because the is Ajax, we need to be asynchronous... which means we need to define a callback method to get the results.
function onTitleChange(title, id) {
Sys.Debug.trace(title + id);
PhotoService.SetPhotoTitle(title, id, onComplete);
}
function onComplete(results) {
Sys.Debug.trace(results);
}
OK... let's run this in FireFox and use FireBug to check out the network traffic.
First thing to notice is that the Sys.Debug.Trace() calls are making their way to firefox's console window. Very helpful in debugging.
Next, we will expand that node and notice the request is in JSON format
As is the response..
Part V: The Ajax Control Toolkit
Now the fact that you can build this is great ajax components is great, but is it even better that we have a large number of pre-built component, ready for you to use. Check out this toolbox, all free and part of VS!
Check out the live demo of all these controls -- Oh, and check them out from Firefox or Safari.. they work the same everywhere!
https://www.asp.net/AJAX/AjaxControlToolkit/samples/
Part VI: ASP.NET Ajax Templates and JQuery
Everything I showed thus far as been shipped and very stable and ready to use... I thought I'd show you some up and coming work.
Check out the ASP.NET AJAX Templates work and the announcement about shipping the very popular Ajax library JQuery with Visual Studio.
First thing let's do is is establish some initial data... In many real world apps, you might use data from a web service call, etc.
var employees = [
{ FirstName: 'Omar', LastName: 'Khan', Email: 'omark@microsoft.com', Photo: 'omar.jpg', Title: 'Product Unit Manager' },
{ FirstName: 'Jeff', LastName: 'King', Email: 'jking@microsoft.com', Photo: 'jeff.jpg', Title: 'Program Manager' },
{ FirstName: 'Pete', LastName: 'LePage', Email: 'plepage@microsoft.com', Photo: 'pete.jpg', Title: 'Product Manager' },
{ FirstName: 'Bertrand', LastName: 'Le Roy', Email: 'bleroy@microsoft.com', Photo: 'bertrand.jpg', Title: 'Program Manager' },
{ FirstName: 'Scott', LastName: 'Guthrie', Email: 'scottgu@microsoft.com', Photo: 'scott.jpg', Title: 'Corporate VP' },
{ FirstName: 'Steve', LastName: 'Ballmer', Email: 'steveb@microsoft.com', Photo: null, Title: 'CEO' }
];
Add the first column, very simply just display the data... This template will repeat for each item in the list.
<div id="column1">
<ul id="simpletemplate" class="sys-template">
<li><b>{binding FirstName}</b><br />
(<span>{binding Title}</span>)</li>
</ul>
</div>
Now we just wire it up
Sys.Application.add_init(
function() {
var simple = $create(Sys.UI.DataView, {}, {}, {}, $get('simpletemplate'));
simple.set_data(employees);
}
);
Now, that is pretty simple, let's try something a bit more fancy.
<div id="fancytemplate" class="sys-template">
<div class="card">
<img sys:src="{{ 'jpg/' + Photo }}" class="icon" />
<b>{binding FirstName}</b> <b>{binding LastName}</b><br />
<span>{binding Title}</span><br /><br />
<a href="{{ 'mailto:' + Email }}">{{ Email }}</a><br />
</div>
</div>
Now, this is good, but I am missing a photo of Steve... Rather than showing a broken image, it would be nice to use a stock image of any pictures that are missing... Let's change up the template a bit. (Notice we are moving away from source code in comments format... stay tuned
<!--* if (Photo) { *-->
<img sys:src="{{ 'jpg/' + Photo }}" class="icon" />
<!--* } else { *-->
<img sys:src="jpg/anon.jpg" class="icon" />
<!--* }*-->
and the results..
Now, for real fun, let's make the data editable...
<table id="edittemplatecontainer">
<thead>
<tr><td>First Name</td><td>Last Name</td><td>Title</td></tr>
</thead>
<tbody id="edittemplate" class="sys-template">
<tr>
<td><input type="text" value="{binding FirstName}" class="short" /></td>
<td><input type="text" value="{binding LastName}" class="short" /></td>
<td><input type="text" value="{binding Title}" class="medium" /></td>
</tr>
</tbody>
</table>
and, of course, we wire this one up too.
var edit = $create(Sys.UI.DataView, {}, {}, {}, $get('edittemplate'));
edit.set_data(employees);
Now, check it out, because of the magic of databding, as I change the values in one view, they show up in all the other views..
In fact, to make the change more obvious, let's do a little color effect on change. And of course we will use JQuery for this.
<script src="js/jquery-1.2.6.debug.js" type="text/javascript"></script>
Then let's define a method to do the color change..
function flashTarget(value, binding) {
var target = binding.get_target();
$(target).parent()
.css('backgroundColor', 'orange')
.animate({ backgroundColor: 'red' }, 500)
.animate({ backgroundColor: 'yellow' }, 1000)
.animate({ backgroundColor: 'white' }, 500);
return value;
}
Notice I get JavaScript intelliense even on JQuery, even in the "chaining"..
Now we just write up this method to each of the bindings... for example:
<li><b>{binding FirstName, convert=flashTarget}</b><br />
(<span>{binding Title, convert=flashTarget}</span>)</li>
Wow -- What a talk! You can find all the demo code here.
Let me know what you think!
Comments
Anonymous
October 20, 2008
PingBack from http://port25.technet.com/archive/2008/10/20/microsoft-at-ajaxworld.aspxAnonymous
October 20, 2008
Awesome post, Brad. It's a keeper. Bookmarked with extreme prejudice. Thanks for all your hard work.Anonymous
October 21, 2008
Hi Brad, Do you have a video of your presentation? Thanks, AshutoshAnonymous
October 21, 2008
Hey Ashutosh - The session was video'ed by Sys-con TV.. I suspect we will see it on their site in a month or so... I will post a link ifwhen it shows up there. You can see a slightly older version here: http://sessions.visitmix.com/?selectedSearch=T34Anonymous
October 21, 2008
:( Great looking Demos.Love to see this but in a month is too long.Anonymous
October 22, 2008
Great post - good use of just about everything. Consider yourself bookmarked.Anonymous
October 22, 2008
The comment has been removedAnonymous
October 22, 2008
The comment has been removedAnonymous
October 23, 2008
Brad, I think you 're missing some code where you're creating the Ajax-enabled WCF service. See the code below this sentence: Now, we need to implement a web services that will update our data model. That block of javascript is repeated again below that.Anonymous
October 23, 2008
How does the ADO.Net Entity Framework support Oracle? Following your instructions in Part II, I don't see the ADO Oracle Provider in the list, only SQL Server via file, compact or standard db.Anonymous
October 23, 2008
Danrdevine - You can find an Orcale provider for EF here: http://code.msdn.microsoft.com/EFOracleProvider Notice that Orcale is working on an "offical" one as well.. But the one above is a great way to get started.Anonymous
October 23, 2008
最近、いろんな方のBlogや記事を見ていて、あ!そっか!なるほど! ということはあの問題はこれで解決できるってことか?と思った記事をちょっと紹介します。 ■今回の注目記事: AjaxWorld TalkAnonymous
October 25, 2008
Haven't wrote much because I am saving up for a big post on Castle Active Record, which I am justAnonymous
October 26, 2008
The comment has been removedAnonymous
October 26, 2008
#.think.in infoDose #4 (20th Oct - 25th Oct)Anonymous
October 27, 2008
I could not find the MySQL support in the Entity Framework? I have VS2008 SP1 and .Net Framework 3.5 SP1 installed. Is it supposed to show up in the Data Provider list? If so, it is not appearing in my list. Have I missed installing something?Anonymous
October 27, 2008
You can find information on the MySql support for Entity Framework here: http://www.mysql.com/news-and-events/web-seminars/display-204.html Hope that helps!Anonymous
October 27, 2008
Brad instructed how to use ASP.NET Ajax Templates on static data. I was thinking why don't I build an example playing with real data? Let's still take the photo application in this article for example. I found it seems Microsoft Ajax template library doesn't work with ScriptManager, so it's impossible to call WCF service with ScriptManager. Here I am gong to use ASP.NET AJAX Client Library for ADO.NET Data Services, another fancy part of ASP.NET AJAX Client Library, which enables you to interact with an ADO.NET Data Service from client script using JSON. Be sure to add [WebGet] to the web methods in the PhotoService.cs as ASP.NET AJAX Client Library for ADO.NET Data Services accesses the services with HTTP GET. Here's the code: <script src="js/MicrosoftAjax.debug.js" type="text/javascript"></script> <script src="js/MicrosoftAjaxTemplates.debug.js" type="text/javascript"></script> <script src="js/DataService.js" type="text/javascript"></script> <script src="js/jquery-1.2.6.debug.js" type="text/javascript"></script> <script src="js/jquery.color.js" type="text/javascript"></script> <script type="text/javascript"> function onTitleChange(title, id) { Sys.Debug.trace(title + id); photoService.query("/SetPhotoTitle?title=" + title + "&id=" + id, onComplete, cbFailure); } function onComplete(results) { Sys.Debug.trace(results); } function cbFailure(error, context, operation) { $get("spanResults").innerHTML = "Error occurred while performing operation " + error; } function flashTarget(value, binding) { var target = binding.get_target(); $(target).parent() .css('backgroundColor', 'orange') .animate({ backgroundColor: 'red' }, 500) .animate({ backgroundColor: 'yellow' }, 1000) .animate({ backgroundColor: 'white' }, 500); return value; } </script> </asp:Content> <asp:Content ID="Content2" ContentPlaceHolderID="Content" Runat="Server"> <div id="Div1"> <div id="template" class="sys-template"> <ul class="photoslist"> <li> <!--* if (ImagePath) { --> <img sys:src="{{ 'Images/' + ImagePath }}" /> <!-- } else { --> <img src="Images/notfound.jpg" /> <!-- } *--> <input id="titleTextBox" type="text" value="{binding Title, convert=flashTarget}" class= "short" onchange="{{ 'onTitleChange(this.value, ' + ID + ');' }}"/> </li> </ul> </div> <asp:Label ID ="spanResults" runat="server" /> </div> <script type="text/javascript"> //ASP.NET AJAX Client Library for ADO.NET Data Services //The method in svc gets called via HTTP GET var photoService = new Sys.Data.DataService("PhotoService.svc"); Sys.Application.add_load( function() { photoService.query("/getAllPhotos", cbSuccess, cbFailure); }); function cbSuccess(result, context, operation) { var simple = $create(Sys.UI.DataView, {}, {}, {}, $get('template')); simple.set_data(result); } </script> </asp:Content>Anonymous
October 27, 2008
Lot's of good stuff today but first here's the new .NET logo and VS2010 & .NET CLR CTP !Anonymous
October 27, 2008
The "conversation" here in the office went something like this: Me (at my desk reading this article): Get Out! Coworker (smiling): No, you get out ~30 seconds later~ Me: Get Out!! Coworker (laughing): No, you get out! ~15 seconds later~ Me: Whoa... Get Out!!! Coworker: Ok... what are you reading? I've read about all these technologies individually, but to see them presented in a succinct article like this almost made me fall off my chair. The javascript proxy to svc was especially impressive, that's some small data transfers. Awesome article Brad!Anonymous
October 28, 2008
Hi, Brad, I saw in some screenshots your were using Mozilla and Firebug. I am wondering if VS 2008 has provided the support of integration with Mozilla. Thanks, billAnonymous
October 28, 2008
In the comments on a recent post a reader asked me how to enabled Firefox in Visual Studio 2008..  Anonymous
October 28, 2008
Bill -- Yes, VS2008 supports using FireFox easily! I just posted on it.. http://blogs.msdn.com/brada/archive/2008/10/28/enabling-firefox-support-in-visual-studio-2008.aspxAnonymous
October 31, 2008
Great post thanks. Bookmarked it so i can rebuild when i have more timeAnonymous
November 03, 2008
Not able to download the source code.Anonymous
November 30, 2008
Do you have any video of this session?Anonymous
December 01, 2008
Fahad -- sorry, no, I have not seen the video posted yet.. I will update here ifwhen I do.Anonymous
December 01, 2008
Haven't wrote much because I am saving up for a big post on Castle Active Record, which I am just really, really enjoying right now in my work. I know we made a good choice. More on that soon. NHibernate/Castle Chad Meyers has posted source/binaries for