ASP.NET 4.0 AJAX - Preview 4 - Data Binding
Throughout the course of my introductory posts on ASP.NET AJAX 4.0, we looked at the new [DataView control][1] as well as the [Sys.Observer class][2], which brings the Observer pattern to plain JavaScript objects. The new ASP.NET AJAX release is very exciting offering powerful new features to take AJAX enabled applications to a new level. In this post, we’ll look at another exciting feature of ASP.NET AJAX 4.0 known as “live bindings.”
You may remember that we looked briefly at live bindings in the [client templates][1] post, but for those examples I used one-way / one-time bindings. Today, we’ll take a closer look at live bindings and see how two-way live bindings removes the one-way / one-time binding restriction allowing us to update bound elements on our page automatically when the underlying data changes.
Again, in this post, I’ll be using Preview 4 of the ASP.NET AJAX Library, which can be downloaded from CodePlex. The Preview 4 version can be used in your applications today (e.g. ASP 3.5, HTML). Keep in mind that these components are still in “preview” mode (meaning no Microsoft support), though they are usable at your own risk. For more information, you can check out the license on CodePlex.
Source Code
You can download and run all of the sample code that you find in this post. The source code contains a Web Site project and uses .NET 3.5 SP1 for the server-side components (e.g. Entity Framework, ADO.NET Data Services) and SQL Server Express 2008.
[][3]
The Simplest Live Binding Example
Let’s start with a very simple example of live binding in action. In the first snippet, we’ll look at binding a very simple client template (the Sys.UI.DataView) using the declarative creation and binding syntax.
<body
xmlns:sys="javascript:Sys"
xmlns:dataview="javascript:Sys.UI.DataView"
sys:activate="simpleForm"
>
<div
id="simpleForm"
class="sys-template"
sys:attach="dataview"
dataview:data="\{\{\{ name: '' \}\}\}"
>
Name: <input id="name" type="text" value="{binding name}" /><br />
<span id="nameDisplay">{binding name}</span>
</div>
</body>
If you read my [client template post][1], most of this code should look familiar. There are a couple of new things in this snippet however. First, you probably noticed the {binding name}. This is the live binding syntax. We’ll look closer at this a bit later, but if you’ve used WPF or Silverlight the syntax should look familiar. The one other thing that I’ve added to make this work is the dataview:data attribute. The double curly-braces syntax in this attribute simply defines an inline expression, and inside of that, I’ve created an inline JavaScript object, which contains one property ‘name’ and the value is just an empty string (I could have just as easily used null). The reasoning behind this is that the DataView needs to initially bind data to something, so based on our binding expressions, both the input and span will be initially filled with the empty “name” property. When you load this page, and enter your name in the textbox, the span will be updated with what you entered in the textbox after you tab away from it. You can see this in action in the clip playing off to the right. ## Using Sys.Binding Imperatively In the last example, we used a DataView along with the binding markup. The binding markup {binding propertyName}
and
can only be used inside client templates (the DataView). What if you didn’t want to use a DataView for something like that last example? Well it’s possible, thanks to the way that binding is handled in ASP.NET AJAX. The binding markup maps back to the component Sys.Binding, which in turn uses [Sys.Observer][2]. We can harness the power of Sys.Binding directly if needed. Let’s see how we would accomplish the same example as before, but this time using Sys.Binding directly. We can do this declaratively (like the DataView example from above) or imperatively using JavaScript. First, we’ll look at the imperative code:
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<!-- Script references removed for brevity -->
<script type="text/javascript">
Sys.Application.add_init(function () {
$create(Sys.Binding, {
source: $get("name"),
path: "value",
target: $get("nameDisplay"),
targetProperty: "innerHTML",
});
});
</script>
</head>
<body>
<div id="simpleForm">
Name: <input id="name" type="text" /><br />
<span id="nameDisplay"></span>
</div>
</body>
</html>
You’ll notice there isn’t much going on in the HTML this time since this isn’t declaratively bound. Looking at the JavaScript block, I’m using the $create shortcut in order to wire up the Sys.Binding component. Since this is a component and not a control like the DataView, the only parameters that need to be passed to $create
is the type (Sys.Binding
) and properties for the Sys.Binding
component (a JavaScript object). The $create call is simply creating an association between the textbox (input) and the span. Again, the results of this code are exactly the same as the initial example that we looked at. Let’s take a closer look at the properties being set in the last example:
source | The source object (the textbox in our case). Note: This is using $get in order to access the element by its Id. |
path | The property name or path to the source property. This is the property value that will be sent to the target (the value property of the textbox in our case). I would have expected this to be called sourceProperty since there is a targetProperty, but this follows the WPF convention. |
target | The target that will be updated based on the source (the span in our case). Again, notice the $get. |
targetProperty | The property name or path for the target. In short, what you want to set (innerHTML in our case). |
You can find a full list of the <a href=”http://msdn.microsoft.com/en-us/library/dd409292(VS.100).aspx” target=”_blank”
properties for Sys.Binding</a
on MSDN. ## Using Sys.Binding Declaratively You just saw the imperative creation of Sys.Binding, but if you’d rather declarative syntax, then this is the example for you. I’m working off the same example so that you can easily see the similarities and differences between the snippets.
<body
xmlns:sys="javascript:Sys"
xmlns:binding="javascript:Sys.Binding"
sys:activate="simpleForm"
>
<div id="simpleForm">
Name:
<input
id="name"
type="text"
sys:attach="binding"
binding:source="{{ $get('name') }}"
binding:path="value"
binding:target="{{ $get('nameDisplay') }}"
binding:targetProperty="innerHTML"
/>
<br />
<span id="nameDisplay"></span>
</div>
</body>
Comparing this code to that of the imperative version, it should be pretty easy to pick out the similarities. Instead of defining the properties for the Sys.Binding
component in code, they are now added as attributes to the textbox. Also, the sys:attach
coupled with the xmlns:binding=”javascript:Sys.Binding”
replaces the first parameter in the $create call from the imperative example. Other than that, there shouldn’t be anything here that looks strange, if it does, refer back to my [client template post][1]. ## Live Binding Syntax Now that you’ve seen an example of live binding in action, let’s take a minute and look at the options available to us with live binding. The first is the binding type defined in MSDN as an “inline expression evaluation.” This is commonly referred to as one-way/one-time when talking about binding, but the double curly braces syntax
is also used to evaluate expressions (for example the attribute
binding:source=”{{ $get(‘name’) }}”
from the last example). When it used as a binding type, such as
{{ name }}
, this simply means that expression is only handled when the template is bound/re-bound. Next up is
one-way and two-way binding. The syntax for these bindings is {binding propertyName}, as you’ve seen before. The syntax is the same since the determination of using one-way or two-way is based on the element itself. One-way binding is the default mode of binding for HTML properties and context. For input controls however, two-way binding is the default. You can see this in action in the first example we looked at, binding within the DataView. As a refresher, here is the pertinent snippet again:
Name: <input id="name" type="text" value="{binding name}" /><br />
<span id="nameDisplay">{binding name}</span>
If the default behavior isn’t desired, for example if you only wanted one-way binding on a textbox, you can specify the <a href=”http://msdn.microsoft.com/en-us/library/dd409321(VS.100).aspx” target=”_blank”
mode</a
of the binding. This can be done within the inline markup as so:
Name: <input id="name" type="text" value="{binding name, mode=oneWay" /><br />
<span id="nameDisplay">{binding name}</span>
We’ll look a bit closer at binding modes in the next section, but one thing to take away from the syntax here is that you can specify properties for Sys.Binding within the HTML markup. Another common property that you may set would be the source. For example, in a master-detail type setup where you have a master DataView and a detail DataView, you may have something along the lines of:
<div
id="detailView"
class="sys-template"
sys:attach="dataview"
dataview:data="{binding selectedData, source={{master}} }"
></div>
You see that syntax a bit later in the Master/Detail example. In addition to mode and source, another property that you’ll often see is the convert and convertBack properties. These allow you to call functions when either updating the target (convert) or updating the source (convertBack). For example:
<label>Inches: </label>
<input
id="inches"
type="text"
value="{binding num, convert=toInches, convertBack=toFeet }"
/>
<br />
<label>Feet: </label>
<input id="feet" type="text" value="{binding num }" />
</div>
In this snippet the Inches textbox will call toFeet when updating the Feet textbox. When the Feet textbox is updated and the binding happens on the Inches textbox, it will first be converted to inches using the toInches method.
Binding Modes
In the last section we looked at the syntax for live binding, and I introduced the mode functionality. The binding modes come from the “enumerator” Sys.BindingMode and contains five options (similar to those in WPF). Let’s take a closer look at each one of them within the context of the HTML markup expressions. Remember these are simply just properties for the Sys.Binding class, so these are the enumerations you can use if you set the mode property via JavaScript instead of doing it in the markup. ### Sys.BindingMode.auto This is the default binding mode that you’ve seen already. Two-way binding on an input control, and one-way binding on a context-type elements such as spans. If for some reason you want to manually set the mode to auto, you can do this as well, but it’s not required.
<div
id="defaultBinding"
class="sys-template"
sys:attach="dataview"
dataview:data="\{\{ name: '' \}\}"
>
<label>Name: </label>
<input id="name" type="text" value="{binding name, mode=auto}" /><br />
<label>Echo: </label>
<span id="nameDisplay">{binding name, mode=auto}</span>
</div>
Sys.BindingMode.twoWay
This is the default binding mode for input controls. In this snippet, I’ve explicitly set both inputs to twoWay, although I could have left the mode specification off since it is the default. In this snippet, when you update either textbox, the other one will be updated with the same value.
<div
id="twoWayBinding"
class="sys-template"
sys:attach="dataview"
dataview:data="\{\{\{ name: '' \} \}"
>
<label>Name: </label>
<input id="name1" type="text" value="{binding name, mode=twoWay}" /><br />
<label>Echo: </label
><input id="nameDisplay1" type="text" value="{binding name, mode=twoWay}" />
</div>
Sys.BindingMode.oneWay Again, this is the default binding
mode for properties and context-type HTML elements (e.g. span, div, etc). You can specify this mode on an input control in order to override the default two-way binding. In this snippet, when you update the Name textbox it will be displayed in the Echo textbox, but changing Echo won’t update Name since the Echo textbox is oneWay.
<div
id="oneWayBinding"
class="sys-template"
sys:attach="dataview"
dataview:data="\{\{\{ name: '' \}\}\}"
>
<label>Name: </label>
<input id="name2" type="text" value="{binding name}" /><br />
<label>Echo: </label>
<input id="nameDisplay2" type="text" value="{binding name, mode=oneWay}" />
</div>
Sys.BindingMode.oneWayToSource
This binding works opposite to the last oneWay binding example. In this case, changes to the Echo textbox will update the Name textbox, but updating Name won’t change Echo.
<div
id="oneWayBinding"
class="sys-template"
sys:attach="dataview"
dataview:data="\{\{\{ name: '' \} \}"
>
<label>Name: </label>
<input id="name2" type="text" value="{binding name}" /><br />
<label>Echo: </label>
<input id="nameDisplay2" type="text" value="{binding name, mode=oneWay}" />
</div>
Sys.BindingMode.oneTime
In oneTime mode, the element will only be updated when the template is first instantiated. For this snippet, the Name and Echo textboxes will be updated with my name and will be bound to both textboxes since it is defined in the object literal being passed to the DataView. Changing the Name textbox in this case won’t update the Echo box.
<div
id="oneTimeBinding"
class="sys-template"
sys:attach="dataview"
dataview:data="\{\{ \{ name:'Damien' \} \}\}"
>
<label>Name: </label>
<input id="name4" type="text" value="{binding name}" /><br />
<label>Echo: </label>
<input id="nameDisplay4" type="text" value="{binding name, mode=oneTime}" />
</div>
The “Classic” Example: Master/Detail
Typically when looking up live binding, you’ll come across some kind of master/detail sample. To keep with the “tradition” we’ll work through an example using SQL 2008, the Entity Framework, and ADO.NET Data Services server-side, and the ASP.NET AJAX DataView client-side. Again, keeping with the trend of seeing how ASP.NET AJAX can be used outside of ASP.NET, the hosting page will simply be a standard HTML page. In this sample we will be keeping a list of blogs that we enjoy. We’ll add the blogs to the page one at a time, and we will persist the changes back to the database all at once thanks to ADO.NET Data Services. ### Server-Side Code First, we’ll start with a simple SQL 2008 Express Database that contains a single table named “blog.” The schema is shown in Figure 1. Figure 1 – The Database Schema Next up is the Entity Framework. Simply add an ADO.NET Entity Data Model to the project. I’ll name this BlogModel.edmx. I’ll then use the “Generate from Database” option when prompted. I’ve accepted the defaults for everything else. We now have a single entity based on the blog table. I’ve changed the EntitySet name to Blogs in order to make our service clearer when calling it, for example to get an item with an ID of 1, /BlogService.svc/Blogs(1) instead of /BlogService.svc/Blog(1) if I had left the default EntitySet name. Figure 2 – The Blog Entity Now that the database and the entity model are in place, the next step is to expose the entities via ADO.NET Data Services. To do this, simply add an “ADO.NET Data Service.” I’ve named my service “BlogService.svc.” Then we need to configure the service, which involves specifying our entities to expose and configuring the rules for our service. Since this is just a test service, I’ve opened all operations to all entities. In addition I’ve configured the service to use verbose errors, and to include the exception details in an error. In a production application, these settings should not be used, as we’re exposing everything to everyone.
[ServiceBehavior(IncludeExceptionDetailInFaults=true)] public class
BlogService : DataService<BlogsModel.BlogsEntities
>{ public static void InitializeService(IDataServiceConfiguration config) {
config.UseVerboseErrors = true; config.SetEntitySetAccessRule("*",
EntitySetRights.All); config.SetServiceOperationAccessRule("*",
ServiceOperationRights.All); } }
Now that the server side is configured, let’s test it out in the browser. One thing you need to do before testing an ADO.NET Data Service is to make sure the “Feed View” is turned off in your browser. For the XML representation of the data, ADO.NET Data Services uses ATOM, which RSS feeds use as well. The browser, being helpful, will use the “Feed View” trying to “pretty” up the display of your service, when in fact we won’t see the raw XML data. To turn off Feed View in Internet Explorer, simply go to Tools » Internet Options » Content Tab » Feed Settings » Uncheck “Turn on feed reading view.” Now we can start browsing our service. Calling the BlogService.svc directly, this will show us the collections available for us to use. Simply browse to “/BlogService.svc” in the root of the application. Next, let’s test the Blogs collection by browsing to “BlogService.svc/Blogs” and finally use “BlogService.svc/Blogs(1)” to access the entry with an ID of 1.
Figure 3 - Testing the ADO.NET Data Service ### Client-Side Code Now that the server work is done, let’s move on to the client code. The user interface will allow us to perform the standard CRUD (Create, Read, Update, Delete) operations using ADO.NET Data Services. We will list the blogs that are found in the database and add a snapshot of the site using a cool service I discovered called BitPixels.com. This site serves thumbnails of web pages without requiring a registration, which is perfect for a demo since you can download my sample code and run this on your machine without having to worry about registering to get it to work. Here’s an example of it fully working: > An important thing to note on the example, changes aren’t persisted until they are saved back to the server. We’ll see how this is accomplished when we look at the JavaScript code. The first difference with this example from the others that you have seen in my previous posts ([Sys.Observer][2] and [Client Templates][1]) is the JavaScript files being referenced. We’re still using MicrosoftAjax.js and MicrosoftAjaxTemplates.js, but in addition we need to reference MicrosoftAjaxAdoNet.js. This library allows us to work very easily with ADO.NET Data Services. The script references should look like the following:
<script type="text/javascript" src="scripts/MicrosoftAjax.debug.js"></script>
<script
type="text/javascript"
src="scripts/MicrosoftAjaxTemplates.debug.js"
></script>
<script
type="text/javascript"
src="scripts/MicrosoftAjaxAdoNet.debug.js"
></script>
Now, onto the HTML markup. There is a fair amount going on here, but
some of it should look familiar to you.