Atlas provides a binding model which differs from the data-binding model in ASP.NET. It is a flexible binding mechanism, somewhat similar to the one provided by WPF (Windows Presentation Foundation), that basically lets you bind any property of 1 component to any property of another object. Therefore we call them bindings, instead of data-bindings, since we can for example even bind the style of 1 control to a property of another control.

Let's first look at a basic example that binds the value of a textbox to a label. Whenever we change the text in the textbox, we want to update the label's text property accordingly.

ASP.NET:
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 

HTML:
[...]
<input id="nameTextBox" type="textbox" /><br />
<span id="nameLabel"></span>
[...]

Atlas:
<script type="text/xml-script">
  <page xmlns:script="http://schemas.microsoft.com/xml-script/2005">
    <components>
      <!-- 'Upgrade' the HTML textbox to an Atlas textbox. -->
      <textBox id="nameTextBox" />

      <!-- 'Upgrade' the HTML span to an Atlas label. -->
      <label id="nameLabel">
        <bindings>
          <binding dataContext="nameTextBox" dataPath="text" property="text" />
        </bindings>
      </label>
    </components>
  </page>
</script>

So, what does Atlas do to make this this possible? First of all, Atlas needs a way to listen to property value changes (unless we don't use automatic bindings). There is an INotifyPropertyChanged interface, which is similar to the one provided by .NET. Objects can implement this interface to let other objects listen to property value changes. The Atlas component class (base class for components such as controls and behaviors) implements this interface. Additionally, the component class has a function 'raisePropertyChanged(propertyName)' which you should call in every property's setter, to raise the INotifyPropertyChanged.propertyChanged event.

With this knowledge, lets look at a few parts of the TextBox implementation to learn how we should raise the propertyChanged event when an event of the HTML element was raised.

JavaScript:
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
31 

[...]
var _text;
var _changeHandler;

this.get_text = function() {
  return this.element.value;
}

this.set_text = function(value) {
  if (this.element.value != value) {
    _text = value;
    this.element.value = value;
    this.raisePropertyChanged('text');
  }
}
this.initialize = function() {
  Web.UI.TextBox.callBaseMethod(this, 'initialize');
  _text = this.element.value;
  _changeHandler = Function.createDelegate(this, this._onChanged);
  this.element.attachEvent('onchange', _changeHandler);
  [...]
}

// Handles changes in the underlying HTML control.
this._onChanged = function() {
  if (this.element.value != _text) {
    _text = this.element.value;
    this.raisePropertyChanged('text');
  }
}
[...]

As you can see, we raise the propertyChanged event whenever the text in our textbox changes. This makes it possible to actually bind against this property.

When we create a binding, the binding will look at both the 'source' object (the data context) and the target object (on which you defined the binding) to see if it implements INotifyPropertyChanged. If the source object does, the binding will handle its propertyChanged event by evaluating the 'incoming' binding, if the property you specified in the dataPath was the property that changed and the direction is set to In or InOut.
If the target object implements INotifyPropertyChanged, its propertyChanged event will be handled by evaluating the 'outgoing' binding, if it is the property you defined on the binding and if the direction is set to Out or InOut.

Lastly, lets have a look at the public properties and methods of a binding, and see what they are good for. I got this list by looking at the getDescriptor implementation of Web.Binding in AtlasCore.js.

  • Property 'automatic': Specifies whether the binding should automatically be evaluated whenever the property of the source object's property was changed (In, InOut), or the target object's property was changed (Out, InOut). Sometimes you want to control when the binding should be evaluated. For example, you only want to evaluate a binding when a ServiceMethodRequest completed the request so it actually has data. You can do this by setting automatic to false, and explictly invoke the evaluateIn method of the binding. The default value is 'true'.
  • Property 'dataContext': Specifies the source object which has a property that you want to bind to. You can set this to another object, such as a TextBox control. If you don't set this, the binding will call its owner's dataContext property. Controls implement this property by either returning the dataContext that was set, or by returning its parent control's dataContext. Controls such as the ListView take care of setting the dataContext to for example a DataRow object when it creates ListView items, which makes it possible to bind against data items.
  • Property 'dataPath': The source object's property that you want to bind to. You can bind against 'nested properties' as well, using the following syntax: 'sourceObjectProperty.nestedProperty.anotherNestedProperty'. The binding will split this path and run code similar to:
      'var value = mySourceObject.getProperty('sourceObjectProperty')
      .getProperty('nestedProperty')
      .getProperty(anotherNestedProperty)'
    The default value is nothing, which means you will actually bind to the source object itself.
  • Property 'direction': Lets you specify whether you want to listen for changes (In), propagate changes (Out), or both (InOut). The default value is 'In'.
  • Property 'property': The target object's property that you want to bind to. You should always set this property.
  • Property 'propertyKey': Sometimes you want to bind to a member of the property you specified in the 'property' property. For example, you may want to bind to the style's property 'width'. You can do so by setting the property 'property' to style, and set the property 'propertyKey' to 'width'.
  • Property 'transformerArgument': An argument that will be passed to a transformer.
  • Event 'transform': This event lets you apply a transformation during the evaluation of a binding. This can be useful if you for example want to display a bound value differently in the target object. There are several built-in transformers, such as Add, which can be useful to add a value (specified using the property 'transformerArgument') to the bound value. This is useful if you are binding to an index, which generally start at 0, and you want to display them starting at 1.
  • Method 'evaluateIn': Evaluates the 'incoming' binding, if the direction was set to In or InOut. It does so by actually getting the value of the source object's property (based on the dataContext and dataPath values), and call the target object property's setter with this value.
  • Method 'evaluateOut': Similar to evaluateIn, except that it works the other way round, and only works when the direction is set to Out or InOut.

Hopefully this answers some questions with relation to bindings. Please leave a comment with any questions that you may have, or leave behind a suggestion for a different aspect of Atlas that you want me to zoom in on.

Perfect Wilco.

Thanks for a nice post on bindings with a good explanation of each element. I look forward to your next post.
Feel free to suggest a topic for my next post. I don't mind if it's a suggestion for building a control and post about it, or if it's about for example how the markup is parsed and the whole page is initialized. Just let me know what you look forward most.
Three questions:

If the direction of a binding is "In" by default, how does that apply to the property "property" which sets the target object's property? It seems like that would be a direction of "InOut" (in through the dataPath, out through the property to the target object?

How might I specify that the href property in a hyperlink tag should get the BrandID from the dataPath and format it to fit in a url? Ex: Brands?id={0}

How might I specify a default for the value property in the setProperty action for when there is no query string variable? See below.
C#:
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 

<hyperLink targetElement="brandsLink">
    <bindings>
        <binding dataPath="BrandID" property="href"/>
    </bindings>
    <behaviors>
        <hoverBehavior>
            <hover>
                <setProperty target="brandsLink" property="cssClass" value='left<%=Request.QueryString("category")%>' />
            </hover>
            <unhover>
                <setProperty target="brandsLink" property="cssClass" value="" />
            </unhover>
        </hoverBehavior>
    </behaviors>
</hyperLink>



This flexible binding is new to me. Do you know of any sites with good examples? Much appreciated,
Keegan
Keegan,

Let me visualize the different bindings in a simplified way:
"In": dataPath -> property
"Out": dataPath <- property
"InOut": dataPath <-> property

You could get a proper URL by using a transformer on the binding to transform a BrandID value into a valid URL.

As for the default value: That's not really supported now as far as I know. You could probably do it by defining a binding and a transformer, and let the transformer return the value or a default value.
>> how the markup is parsed and the whole page is initialized

That would be a great post actually. If you could do that, that would be great.

- Paul
How would I do this without javascript? Or is this javascript required for Atlas?
Steve: Atlas is built on top of JavaScript. The declarative model translates into normal JavaScript objects when Atlas processes the markup.
Thanks. Although I'm very confused on how this works. ie. if what you say is correct, could you show me how to reproduce your sample with no javascript? This declarative model is quite confusing to me actually. I am so accustom to using asp.net control and setting their properties and events that this model makes my brain hurt :)

One common task I'd like to see done in Altas is where you load a dropdown, and then on the selection of the dropdown a sortable, pageable grid displays data - all done in Atlas.

Nice work,
i tried to bind a url to an atlas:hyperlink control, but it never works.
Is it wrong to use the "NavigateUrl" property of the hyperlink control?
The text property is working fine.


I have the same experience as Karsten.
Further to the above:

I mean the URL displays fine and looks like a url, but if you click it, it doesn't take you anywhere.

Looks like the text of the URL is being set correctly, but the Href is not being filled in.

This seems to be an error in the Hands on labs no. 5. - URLs display fine, but don't navigate anywhere, using the code provided in the lab.

No documentation to say what property of the <atlas:hyperlink> to set in the binding to make it work:

I tried: NavigateURL, Href, Alink, URL -> none of which work.

In other words what goes in place of the ????? below:

<atlas:hyperlink runat="server" id="companyUrl" >
<bindings>
<atlas:Binding DataPath="URL" Property="????????" />
</bindings>
</atlas:hyperlink>


Hey Hey !!!

Nothing like good old TRIAL AND ERROR :-

Found one that worked!!! - And it looks like its CASE SeNsItIvE

<atlas:hyperlink runat="server" id="companyUrl" >
<bindings>
<atlas:Binding DataPath="URL" Property="text" />
<atlas:Binding DataPath="URL" Property="navigateURL" />
</bindings>
</atlas:hyperlink>
That is correct. Next time you could take a look at the Atlas object browser I wrote to get the exact names of properties to which you can bind.
Since the January release, none of this seems to work any more.

Is the class browser at

http://www.wilcob.com/AtlasClassBrowser/

updated to the January release - is there a different one for this ?

As far as I can see there is no <atlas:datasource>, <atlas:hyperlink> etc. in the January release.
So you have to go the XML script route.
Using the XML script route I can make things that look like hyperlinks and mouse-over like hyperlinks, but when you click them they just refresh the page.

What am I missing?

Many thanks.


Ha!

Isn't that just the way.
Make a posting, and then 2 minutes later you figure out the answer:

This works:

This in the body:
<div id="masterTemplate">
<div id="masterItemTemplate">
<asp:LinkButton id="masterURL" runat="server"></asp:LinkButton>
etc.

And this in the XML script:
<hyperLink targetElement="masterURL">
<bindings>
<binding dataPath="URL" property="text"/>
<binding dataPath="URL" property="navigateURL"/>
</bindings>
</hyperLink>

> "Using the XML script route I can make things that look like hyperlinks and mouse-over like hyperlinks, but when you click them they just refresh the page."

It should work if you set the href on your hyperlink to something like "#", too.
Is it possible to create a bindable business object in JS by inheriting from Sys.Component?

I've got a business object coming back from a modal dialog that was created by setting the returnValue to a serialized JSON string of a server object (thx MSFT for the serialization function). I need to bind its data to a series of labels (spans).

My current theory on how to accomplish this is to define my business object in JavaScript inheriting from Sys.Component (or implementing INotifyPropertyChanged, I'm not sure), and my business object should be bindable. Then, I take my business object that was returned from the modal dialog and create it's bindable version. This is a lot like how in Atlas you do "
JavaScript:
1 

var x = new Sys.Web.Textbox($('textbox1'));

" I'll probably do something like "
JavaScript:
1 

var keyword = new BindableKeyword(businessObject);

"

Then I just use Sys.Binding as I normally would. It works in my head ... but then again I'm better looking and wealthier in my head too.

Any thoughts?
Yes, you should indeed be able to derive from Sys.Component. As long as Atlas can get a type descriptor for your object you should be good.
Wilco, thanks for confirming this. I went ahead and put together a POC and sure enough, it worked like a champ.

I've got a blog post that details what I went through to create the bindable business object and at the end I specify some caveats of the approach. I'm wondering if you wouldn't mind reading through it and letting me know what you think. Without recreating the the whole blog entry the biggest problem I have is that to create a bindable business object I have to define the class within JavaScript when I've already defined the class in .NET. I've got a maintenance and object creation nightmare if this is how I have to do it.

The blog's at http://blogs.interknowlogy.com/JoelRumerman.

Much appreciated! - Joel
Wilco, thanks. See my comments at my blog ...

Again, thanks.
Your message will be encoded/formatted when it is displayed. If you want to post code, please put the code inside [code=X][/code] tags, where X is the language of your code (C#, ASPX, SQL, etc).
Name:
Email:
(will be encoded using JavaScript to keep it functional and prevent it from being picked up by spammers)
Url:
 
Message:
3 + 3 =