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

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

14 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.

Lol @ all the sex spam *gg Hmm to a beginner

Jovi | Monday, 13 April 2009 | 4:03 pm

Lol @ all the sex spam *gg

Hmm to a beginner ike me, creating an empty setter is quite tempting, since its so easy that i figured it out myself. While your better approach that throws compiel time errors needs to be grasped as a concept first – but I guess without “grasping concepts” as a beginner in Flex, I won’t get too far :) cheers

Thanks, I normally make setters that "do nothing" but those properties

Keith H | Monday, 07 December 2009 | 8:34 pm

Thanks,

I normally make setters that “do nothing” but those properties will look both read and writable to getClassInfo. Not something I want to do since I use that method a lot.

This way with propertyChange is more useful for me.

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

del.icio.us-ed

  • samuel's squawk at master - GitHub
  • Pixelwave - A native 2D iPhone framework, based on the Flash API
  • Pixelwave - A native 2D iPhone framework, based on the Flash API
  • mnot’s Weblog: Are Resource Packages a Good Idea?
  • Download details: IE App Compat VHD
  • ZSync
  • jQuery source viewer
  • Penetration testing tools - Stack Overflow
  • Logrep
  • DOM Window (jquery.DOMWindow.js)

Recent Posts

  • Moving on
  • iPhone / iPod Touch Development Resources
  • Upgrading your app to AIR 1.5
  • Motivate yourself by doing it in public
  • The trouble with Flash and REST
rss Comments rss valid xhtml 1.1 design by jide powered by Wordpress get firefox