Laptop

Throughout the course of my introductory posts on ASP.NET AJAX 4.0, we looked at the new DataView control as well as the Sys.Observer class, 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 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.

Laptop

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>

Databinding Sample

If you read my client template post, 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 {{ propertyName }} 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. 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 properties for Sys.Binding 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.

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 ``, 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 mode 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}} }">

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 }" />

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 01

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 02

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 02

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:

Figure 04

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 and Client Templates) 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.

 1<body   xmlns:sys="javascript:Sys"
 2        xmlns:dataview="javascript:Sys.UI.DataView"
 3        sys:activate="*">
 4    <div class="tools">
 5        <input type="button" value="Add" onclick="addBlog()" />
 6        <input type="button" value="Save" onclick="BlogService.dataContext.saveChanges()" />
 7        <input type="button" value="Reload"
 8            onclick="$find('blogList').fetchData(null, null, Sys.Data.MergeOption.overwriteChanges)" />
 9    </div>
10    <div id="blogList" class="sys-template"
11        sys:attach="dataview"
12        dataview:dataprovider="{{ BlogService.dataContext }}"
13        dataview:fetchoperation="Blogs"
14        dataview:ondataloading="{{ dataLoading }}"
15        dataview:autofetch="true"
16        dataview:selecteditemclass="blogSelected"
17        dataview:initialselectedindex="0"
18        dataview:sys-key="master"
19        dataview:oncommand="{{ dvCommand }}">
20        <div class="blogBlock" sys:command="Select">
21            <div class="blogTools">
22                <div class="blogDelete" sys:command="Delete"
23                    sys:commandargument="{{$index}}">X</div>
24            </div>
25
26            <img alt="{binding Name}" height="75" width="100"
27                sys:src="{binding Url, convert=toSnapshot}" />
28            <hr />
29            <a href="{binding Url}">{binding Name}</a>
30        </div>
31    </div>
32
33    <hr style="clear:left;" />
34
35    <div id="detailView" class="sys-template detailsForm"
36        sys:attach="dataview"
37        dataview:data="{binding selectedData, source={{master}} }" >
38        <label>Name: </label><input type="text" value="{binding Name}" /><br />
39        <label>Url: </label><input type="text" value="{binding Url}" />
40    </div>
41</body>

Let’s go through the code in chunks

  • Lines 1 – 3: This shouldn’t be anything new, you first saw this when working with the DataView declaratively.
  • Lines 4 – 9: This section defines the buttons that allow us to Add, Save, and Reload.
    • Line 5: For adding, I’m calling a custom JavaScript function which you’ll see in a moment, addBlog().
    • Line 6: Using the ADO.NET Data Context, we can persist the changes back to the database. Instead of simply defining a variable (e.g. dataContext), I created a simple JavaScript object (BlogService) to hold the properties that we need to work with (dataContext, which is a Sys.Data.AdoNetDataContext, and an observable array “data” that is made observable with Sys.Observer.makeObservable). You’ll see this in the JavaScript code.
    • Line 7 & 8: This input button reloads the data. This uses the method “fetchData” that is found on the DataView object. The $find shortcut gets the DataView from the blogList DIV element, allowing us to call fetchData. It takes four parameters, a success callback, failed callback, merge option, and a user context. For the sake of reloading the DataView, the first two parameters (the callbacks) are null since the DataView handles those for us. The third is a type of Sys.Data.MergeOption and we want to reload everything from the server. Finally, we don’t need to specify anything for the user context, so that parameter is excluded.
  • Lines 10 – 19: This is the setup of our “master” div. This displays the website thumbnails, contains a delete button, and allows us to select an item.
    • Line 10: Nothing new here, just an id and a class of “sys-template.”  Remember, this class needs to be on all of your DataViews and you should have a style defined as “display: none;” for this. Doing this permits the AJAX library to show the content only after it has been bound.
    • Line 11: You’ve seen this many times before, we’re simply attaching the DataView “behavior” to the div.
    • Line 12: Now we begin setting the properties we need for the DataView. Here we are telling the DataView to use a dataProvider. The provider here is a Sys.Data.AdoNetDataContext and is defined in our JavaScript code. I’ve assigned it to a simple object, “BlogService” to keep the global variables in one place.
    • Line 13: The fetchOperation is used to define the entity set (from ADO.NET Data Services) that we want to display. There is only one in this example and that is “Blogs.”  Recall we accessed this earlier using the browser (e.g. “BlogService.svc/Blogs”).
    • Line 14: This is an important event that is being wired up dataLoading via the onDataLoading method. This fires when, you guessed it, the data loads. Here we’re calling a custom JavaScript function named “dataLoading” that I have defined in the page. Within this function that you’ll see in the JavaScript code below, the data is put into an observable collection. This collection is where our changes will take place and how the DataView and the live bindings actually do the automatic updates. All thanks to the Sys.Observer object.
    • Line 15: You saw this back in my client templates post when using the WCF service. Setting autoFetch to true is used when we have a dataProvider. This tells the DataView to retrieve the data when the page loads.
    • Line 16 & 17: The DataView has built-in functionality for handling item selection. In line 16, we can define a CSS class for the item when it is selected (selectedItemClass). In line 17, we are telling the DataView to initially select the first item in the list when the page loads (initialSelectedIndex).
    • Line 18: **The purpose of **sys-key is to create a local variable that is a reference to the current component. This allows us to refer to it (line 37), without needing to use $find. (Thanks to Bertrand Le Roy for pointing this out)
    • Line 19: This may look familiar from server-side code that you have written (e.g. DataGrid). The onCommand method allows us to handle commands that are raised from the component. The reason for handling this is the custom “delete” command on Line 22.
  • Lines 20 – 31: This is the master DataView’s content.
    • Line 20: The first of two commands that we need to handle. Select is a command that the DataView handles automatically for us. When you click inside the div, the DataView handles this event by setting the selectedData property (which will be used in the edit form on Line 37), setting the selectedIndex property, and also setting the selectedItemClass.
    • Line 22 & 23: The second command that we need to handle is delete. To do this, we set the sys:command to “Delete” on Line 22. This command will be bubbled up to our custom handler since we set the onCommand method of the DataView on Line 19. In addition to custom events, we can have custom event args. {{ $index }} evaluates to the index of the item as generated by the underlying collection.
    • Line 27: This line is of interest since we are using the convert property of the binding. Because of this, the src attribute needs to be prefixed with the sys namespace. In some instances like this one, we need to prefix the attribute with sys in order to obtain the full functionality. The convert function specified here is a custom one that will either set an empty image or change the src to the one that grabs the website snapshot.
  • Lines 35 – 40: This is the “detail” DataView, or in our case the edit form.
    • Line 37: This is really the only item of interest in the edit form since everything else is pretty standard. Here the binding is a bit special. We are binding to the selectedData property of the “master” DataView. The refers to the sys-key defined in Line 18.

Now that you fully understand the HTML, let’s move onto the JavaScript. For the JavaScript I have added all of the comments inline instead of a full discussion after the fact to make it easier.

// Create an empty object to store global variables
//   - dataContext: The AdoNetDataContext
//   - data: An observable "view" of the data
var BlogService = {};
 BlogService.dataContext = $create(
    Sys.Data.AdoNetDataContext,
    { serviceUri: "BlogService.svc" }
);

function dataLoading(sender, args) {
    // BlogService.data is where our changes will take place
    // You can think of this as a "ViewModel"
    // See http://forums.asp.net/p/1405579/3060650.aspx for more info
    BlogService.data = args.get_data();
    Sys.Observer.makeObservable(BlogService.data);
}

function addBlog() {
    // Create a new Blog entity
    var newBlog = { Name: 'Blog Name', Url: '' };

    // Add the blog to the DataContext
    BlogService.dataContext.insertEntity(newBlog, "Blogs");

    // Add the blog to our "ViewModel"
    BlogService.data.add(newBlog);
}

function toSnapshot(url) {
    // Either return a blank image
    if (url.substring(0, 7) !== 'http://')
        return 'nothumbnail.jpg';
    // Or use the bitpixels.com path to get a thumbnail
    return 'http://img.bitpixels.com/getthumbnail?url=' + url;
}

function dvCommand(sender, args) {
    // Test for our custom delete command
    if (args.get_commandName() === 'Delete') {
        // Get the index that we set on the delete button
        var index = args.get_commandArgument();
        // Get the entity from the local data
        var deletedBlog = BlogService.data[index];
        // Remove the entity from the ADO.NET Data Service
        BlogService.dataContext.removeEntity(deletedBlog);
        // Remove from the local collection
        BlogService.data.remove(deletedBlog);
        // Set the DataView's selected index
        if (index >= BlogService.data.length) index--;
        $find('blogList').set_selectedIndex(index);
    }
}

Conclusion

ASP.NET 4.0 is one exciting release. The ASP.NET AJAX features shown here are incredibly cool. The best thing about ASP.NET AJAX is that we can start using the scripts today in our ASP.NET 3.5 SP1 applications thanks to Preview 4. No need to install anything on the server, just simply reference the scripts. I’m excited to see documentation has been released for ASP.NET 4.0 Beta 1 and I would recommend checking out the ASP.NET AJAX Client Reference on MSDN. I’ve also included many links to it throughout this post. Another helpful reference is the ASP.NET 4.0 and VS 2010 whitepaper, where you can see all the features of ASP.NET 4.0 as well as the ASP.NET AJAX functionality.

For other blogs on ASP.NET AJAX 4.0, be sure to check out: