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!
http://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' }
];
<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!
Tidak ada komentar:
Posting Komentar