Dynamic Flash

Confessions of a serial code abuser
  • rss
  • Home
  • MTASC
  • Archives
  • About me
  • Goodies
    • Base64 encoder/decoder class
  • My Bookshelf
  • My Talks

Databinding to read-only properties in Flex 2

Sunday, 10 December 2006

Databinding has to be just about one of the coolest features in Flex 2, allowing you to bind a property to the value of a property of another object. When the property of that object is updated, the value of the bound property will automatically be updated. This is mainly used in Flex to update UI components when the data for the model changes.

Unfortunately databinding doesn’t work for read-only properties. A read-only property (by my definition) is a private instance variable of a class that has a getter function without a matching setter function. Something like this:

package {
  public class User() {
 
    public function User(name:String):void {
      this._name = name;
    }
 
    private var _name:String;
 
    [Bindable]
    public function get name() {
      return this._name;
    }
 
    public function addTitle(title:String):void {
      this._name = title + " " + this._name;
    }
  }
}

Here the name property of the User class is read-only from outside of the class. This is useful if you won’t want other developers (or other classes) from messing with the values of an object, and in doing so helps you to property encapsulate your data and logic. I’ve also concocted an addTitle method that changes the value of the name property, just so we have something to call to demonstrate databinding.

To make the name property bindable I’ve added the [Bindable] metadata tag just before the getter function. The [Bindable] metadata tag is used to enable properties to be used as the source of databinding operations, and the Flex compiler picks up on this metadata tag and adds in some extra code to your class to implement data binding at compile time.

This looks as though it might work, but sadly it doesn’t. You’ll get the following warning from the compiler:

[Bindable] on read-only getter is unnecessary and will be ignored.

While I disagree whole-heartedly with that statement - if it was unnecessary we wouldn’t be trying to bind to it - I can see where they’re coming from. (Note to Adobe: This is what happens when you let programmers write error messages). The reason it doesn’t work is that databinding requires a matching setter function - and for that setter function to be used internally whenever changing the property value - in order to detect changes. As mentioned earlier, the Flex compiler adds code to your class that fires an event whenever the setter function is invoked. Without this event there’s no way for anything bound to that property to know it has changed.

The solution to this problem is to define your own databinding event and manually dispatching that event whenever you change the private property. You specify the event name as part of the [Bindable] metadata tag, and you’ll need to have your class extend EventDispatcher or one of its sub-classes to use the dispatchEvent method to dispatch your event.

There’s an event specifically defined for this purpose: PropertyChangeEvent.PROPERTY_CHANGE. This is the same event type that Flex uses internally when it generates the data binding code, so if it’s good enough for Flex it’s good enough for us. This class has a static createUpdateEvent method where you can supply the necessary properties - the object dispatching the event, the name of the property changed, the old value and the new value - and get back a PropertyChangeEvent object you can pass to dispatchEvent.

package {
  import flash.events.EventDispatcher;
  import mx.events.PropertyChangeEvent;
 
  public class User() {
 
    public function User(name:String):void {
      this._name = name;
    }
 
    private var _name:String;
 
    [Bindable(event="propertyChange")]
    public function get name() {
      return this._name;
    }
 
    public function addTitle(title:String):void {
      var oldName:String = this._name;
 
      this._name = title + " " + this._name;
 
      if (oldName !== this._name) {
        this.dispatchEvent(PropertyChangeEvent.createUpdateEvent(this, "name", oldName, this._name));
      }
    }
  }
}

Notice that I’m checking to see that the value of the name property really has changed before dispatching the event, which means the databinding mechanism isn’t unnecessarily invoked. In this case it wasn’t strictly necessary since we know that the property will always have changed, but it’s a good habit to get into.

This looks a bit messy though, and it’ll only get worse as you discover more places in your code where this property needs to be updated. What I do is add a private setPropertyName method that I can use whenever I want to change the properties value:

package {
  import flash.events.EventDispatcher;
  import mx.events.PropertyChangeEvent;
 
  public class User() {
 
    public function User(name:String):void {
      this._name = name;
    }
 
    private var _name:String;
 
    [Bindable(event="propertyChange")]
    public function get name() {
      return this._name;
    }
 
    private function setName(val:String):void {
      if (this._name !== val) {
        var oldName:String = this._name;
        this._name = val;
        this.dispatchEvent(PropertyChangeEvent.createUpdateEvent(this, "name", oldName, val));
      }
    }
 
    public function addTitle(title:String):void {
      this.setName(title + " " + this._name);
    }
  }
}

There, much better.

Using the PropertyChangeEvent class has another added benefit, which is that ArrayCollection objects (and possible others) listen out for this event on the items they contain and broadcast their own databinding events. If we’d used a custom Event class, the containing ArrayCollection wouldn’t have known anything had changed and any UI control bound to that ArrayCollection would not have been updated.

I’ve created a simple demonstration of this technique in action (view source) which you can also download.

Update

Having just ran the above example if debug mode I’ve noticed that you get a “warning: unable to bind to property ‘name’ on class ‘User’” in the console, which I thought was a bit strange since the example works just fine.

It turns out that Flex is trying to set up 2-way databinding so that any changes in the UI components are reflected in the binding source object (i.e. User). Because our User class is read-only, Flex cannot set up the reverse binding, and that’s where the warning is coming from.

The moral of the story is that you can ignore these warnings if they’re given on read-only properties.

Categories
Flash, Flex 2
Comments rss
Comments rss
Trackback
Trackback

« Speaking about E4X at LFPUG Where is Flash Debug Player v9.0.28.0 for Intel OS X? »

17 responses

you probably want to do the same to title, because

ilya | Saturday, 09 December 2006 | 5:21 pm

you probably want to do the same to title, because when it is set, your name is updated and you probably want to see that reflected in the ui components that display the name…

oh, you did. sorry misread the code :)

ilya | Saturday, 09 December 2006 | 5:21 pm

oh, you did. sorry misread the code :)

but if setTitle is public why not make setName public

ilya | Saturday, 09 December 2006 | 5:22 pm

but if setTitle is public why not make setName public as well… Its a bit of a contrived example…

ilya: The example isn't taken from production code, but it's

Steve | Saturday, 09 December 2006 | 5:49 pm

ilya: The example isn’t taken from production code, but it’s not entirely unrealistic either. It’s quite common to have a read-only property that is manipulated through other public methods, as is the case here. It allows you to control how the variable can be updated, rather than just giving external code free-reign over your property value.

why don't you make _name Bindable instead? wouldn't that accomplish

yo | Saturday, 09 December 2006 | 9:48 pm

why don’t you make _name Bindable instead? wouldn’t that accomplish what you want?

Steve, your right. I was thinking about it over the

ilya | Sunday, 10 December 2006 | 9:34 am

Steve, your right. I was thinking about it over the day and came to the conclusion your post did make sense. Sorry for my previous ramblings :)

Yo, the concept steve is trying to convey is how to bind to an example that is NOT public…

I know an good example when this type of structure is necessary: imagine a slider and an input field bound to the same attribute on a model. Imagine the slider is stepped. You want the slider to show a rounded value (the stepped), and the input the actual value. you need to work with private variables and an asymetric setter. Just like steve shows.

Thanks for posting this, I finally have an explanation for

Rachel maxim | Monday, 29 January 2007 | 7:11 pm

Thanks for posting this, I finally have an explanation for why I’m getting this annoying warning…and a solution too! Appreciate it.

WARNING: This is nasty, really, but it works and is

Rasheed | Sunday, 11 February 2007 | 3:55 am

WARNING: This is nasty, really, but it works and is to my eye more readable and less cluttered.

You can create a read only property that [Bindable] won’t complain about:

[Bindable] public function get myProp():String { return _myProp; } private function set myProp(value:String) { _myProp = value; }

I don’t know why its allowable but it is :)

For Rasheed's post I would guess that reflection during compilation

thomas | Sunday, 25 February 2007 | 5:05 pm

For Rasheed’s post I would guess that reflection during compilation sees only if there exists a “set” and removes the complaint, but would subsequently encounter an access runtime error if it were actually invoked (which doesn’t happen in this example)

Actually, with Rasheed's post the myProp setter function will silently

Steve | Tuesday, 14 August 2007 | 5:16 pm

Actually, with Rasheed’s post the myProp setter function will silently be made public by the Flex compiler, since you can’t have a getter and setter with different scopes. At least, this is what happens in Flex 2 - I haven’t tested this with Flex 2.01 or Flex 3, so your mileage may vary.

Seems to me there's an easier way: [Bindable] public function get myProp():String

Jonathan Buhacoff | Thursday, 15 November 2007 | 4:53 am

Seems to me there’s an easier way:

[Bindable] public function get myProp():String { return _myProp; } public function set myProp(value:String) { // do nothing, since the property is publicly read only, or… is it possible to throw exceptions from setters ? }

private function businessLogic() { // update _myProp as needed }

You can throw an exception from a setter, but that's

Steve | Monday, 19 November 2007 | 8:44 am

You can throw an exception from a setter, but that’s a runtime error that you may or may not notice before your product ships. And failing silently, as in your example, might lead to code that’s difficult to debug when you forget that the property was supposed to be read-only.

If you create a read-only property in the way I’ve shown, you’ll get a compile-time error when you try to set that property.

nude naked soccer moms

ndiqolaljo | Wednesday, 12 December 2007 | 1:51 pm

nude naked soccer moms

hot latino chicks

zodsydnymqo | Friday, 21 December 2007 | 12:22 am

hot latino chicks

adult friend finder login

mvegtodiwk | Sunday, 23 December 2007 | 4:04 pm

adult friend finder login

nude pics of vanessa hudgens

pascixqanwaj | Thursday, 11 September 2008 | 4:33 am

nude pics of vanessa hudgens

As adults, date rape and the time. Other than

ihysrennihb | Sunday, 19 October 2008 | 10:44 am

As adults, date rape and the time. Other than that she came her again.

Leave a comment

You can use these tags : <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="">

About Dynamic Flash

Steve Webster is a Senior Web Developer for Yahoo! in London, UK.

He is more than a little concerned that he defines himself in terms of his career, and that he talks about himself in the third person.

Find out more

Sponsored Links

del.icio.us-ed

  • Lorem 2: An all-around better Lorem experience.
  • Web-Based UML Sequence Diagram / MSC Generator
  • On Static Media and Django
  • The Economist style guide
  • git-issues
  • as3growl - Google Code
  • A List Apart: Articles: Setting Type on the Web to a Baseline Grid
  • 24 ways: Compose to a Vertical Rhythm
  • minify - Google Code
  • iPhone / iPod Touch Backup Extractor

Recent Posts

  • Upgrading your app to AIR 1.5
  • Motivate yourself by doing it in public
  • The trouble with Flash and REST
  • And we’re back
  • Slides and example code from <head> ‘08

Tags

Accessibility ActionScript actionscript3 actionscript 3.0 air apple astro book calendar conference designer filereference file upload Flash flex fotb08 framework getters head head08 head2008 headconference html5 http ical internet internet explorer JavaScript jobs junior microsoft opportunities representational state transfer rest ria schedule silverlight singularity08 skin tutorial urlrequest web developer web development Web Standards Yahoo!

Stuff

Singularity?
Flex.org - The Directory for Flex
rss Comments rss valid xhtml 1.1 design by jide powered by Wordpress get firefox