New Features in ASP.NET AJAX 4, Part 2

Instantiating Behaviors and Controls Declaratively

ASP.NET AJAX 4 introduces a way to declaratively instantiate client-side controls and behaviors and attach them to HTML elements. The declarative markup is achieved without adding any new HTML elements, simply by including additional namespaced attributes which are XHTML compliant.

Declarative Instantiation Within a Template

Suppose that you want to add a Contoso.DatePicker control to a div element. In ASP.NET AJAX 4, you can start by declaring a namespace prefix in the opening <body> tag (or in a template's parent tag), similar to the way the @ Register directive works in server-based files. The following example shows a namespace declaration.

<body xmlns:sys="javascript:Sys" xmlns:datepicker="javascript:Contoso.DatePicker">

The javascript:Sys namespace (typically mapped to the sys: prefix, as shown in the example) is used for a number of system attributes. One of those system attributes is sys:attach, which is used to specify a comma-separated list of controls or behaviors to attach to the element, as shown in the following example:

<ul id="myTemplate" class="sys-template">

<li>

<h3>{{ Name }}</h3>

<div>{{ Description }}</div>

<div sys:attach="datepicker" datepicker:date="{{ CreatedDate }}"></div>

</li>

</ul>

The example shows how to instantiate a Contoso.DatePicker control that is attached to a div element and how to set the control's date property to the value of the CreatedDate field of the current data item.

Declarative Instantiation Outside a Template

Declarative instantiation of controls will only work if the declarative markup is within an element that has been configured, or activated, for this purpose. Templates themselves are already activated, so declarative markup to instantiate and attach controls works within a template. But to declaratively instantiate controls outside a template, you must first configure the page to ensure that sys:attach markup is within an activated element. You do this by including a sys:activate attribute on the opening body tag, and setting its value to a comma-separated list of element IDs for the elements that you want to allow declarative instantiation in. The following example activates elements whose IDs are panel1 and panel2:

<body xmlns:sys="javascript:Sys" sys:activate="panel1,panel2">

This causes the ASP.NET AJAX framework to scan the children of those elements for any sys:attach attributes, and to instantiate the corresponding controls.

You can also activate every element in the document. However, doing so can cause a small delay when the page is initialized, so the technique should be used with caution on large pages. The following example shows how to activate the whole document.

<body xmlns:sys="javascript:Sys" sys:activate="*">

A DataView control is typically attached to an element that is not already within a template. This is a common reason to use sys:activate, as in the following complete example:

<body xmlns:sys="javascript:Sys"

xmlns:dataview="javascript:Sys.UI.DataView"

sys:activate="*">

<ul sys:attach="dataview" class="sys-template"

dataview:data="{{ imagesArray }}"

>

<li>

<h3>{{ Name }}</h3>

<div>{{ Description }}</div>

</li>

</ul>

</body>

Live Data Binding

The template examples shown earlier include several examples of data binding that uses the one-way/one-time binding syntax: {{ expression }}. This is illustrated by the following example:

<h3>{{ Name }}</h3>

This type of binding is referred to as one-time binding because the expression is evaluated only once, at the time that the template is rendered. With one-way/one-time binding, if the source data changes after the template has been rendered (in the previous example, if the Name field changes), the rendered value will not be automatically updated.

You can use an alternative live-binding syntax in order to ensure that the target value is automatically updated whenever the source value changes. This is shown in the following example:

<h3>{binding Name}</h3>

With this binding syntax, if the Name field of the current data item is changed, the rendered value will automatically be updated in response.

This example, where the binding is to a text node (the content of the h3 element), illustrates one-way live binding. The source value (in this case, the data) binds one-way to the target (in this case, an HTML text node), so when the source value changes, the target is updated. But there is no binding from the target back to the source.

Another form of live binding is two-way live binding, which is useful when a text box is provided that enables users to modify the value of underlying data, as in the following example:

<input type="text" value="{binding Name}"/>

In two-way live binding, the binding works in both directions. If the target value is changed (in this case, the value in the UI), the source value is automatically updated (in this case, the underlying data item). Similarly, if the source value is changed (in this case, if the underlying data value is updated externally), the target value (the value in the UI) is updated in response. As a result, target and source are always in sync.

In the following example, if the user modifies the value in the text box, the value that is rendered in the h3 element will automatically be updated to reflect the new value that is provided by the user.

<h3>{{ binding Name }}</h3>

<input type="text" value="{binding Name}"/>

The live-binding syntax is similar to binding syntax in WPF (XAML). It can be used for binding between UI and data, as in the above examples, as well as directly between UI elements, between data and properties of declaratively attached controls and components, and so on. The syntax also supports additional features, such as providing functions for converting data values to rendered values, or converting back from values entered in UI to an appropriately formatted data value. The following example shows how to provide conversion functions:

<input type="text" value="{binding Price, convert=toDollars, convertBack=fromDollars}"/>

A similar syntax can be used to specify a binding mode (one-way or two-way) explicitly:

<input type="text" value="{binding Price, mode=oneWay}"/>

However, in most cases this is not necessary, because the default binding behavior is that text boxes and other input controls automatically use two-way binding, and other bindings use one-way binding. In the previous example, this default behavior is overridden so that if the data value changes, the value in the text box will change, but if the user modifies the value in the UI, the underlying data value will not be updated correspondingly.

The underlying technology that enables live bindings is the ASP.NET AJAX observer pattern, which is used internally by the Binding class and is described in the next section. For more information about binding, see The DataView Control later in this document.

Using the Observer Pattern with JavaScript Objects and Arrays

The observer pattern enables an object to be notified about changes that occur in another object. (The term observer pattern is often misused in JavaScript frameworks to describe event handling based on the addHandler method and similar techniques.) ASP.NET AJAX 4 implements the pattern completely. It adds observer functionality to ordinary JavaScript objects or arrays so that they raise change notifications when they are modified through the Sys.Observer interface. (In the present state of JavaScript, changes that are made directly, without going through the interface, will not raise change notifications.) The observer pattern can be used to establish live bindings between UI elements and objects or arrays, such as those you might get from a JSON Web service.

In the following example, the Sys.Observer class is used to add items to the imagesArray array in a way that raises CollectionChanged notifications. As a result, the DataView control will automatically update and display the inserted item after the user has clicked the Insert button. This is possible because the DataView control is bound to its source data (in this case, the imagesArray array that the data property is set to) by using live binding.

<script type="text/javascript">

var imagesArray = [];

Sys.Observer.makeObservable(imageArray);

function onInsert() {

var newImage = { Name: "Name", Description: "Description" };

imagesArray.add(newImage);

}

</script>

<button onclick="onInsert()">Insert</button>

<ul id="imagesList" sys:attach="dataview" class="sys-template"

dataview:data="{{ imagesArray }}"

>

<li>

<h3>{{ Name }}</h3>

<div>{{ Description }}</div>

</li>

</ul>

Posted in: asp.net | Tags: asp.net asp.net 4.0 asp.net ajax ajax instantiation declarative xmlns declarative instantiation javascript live data binding observer pattern