« I'm on The Flex Show | Main | Boston Flash Platform User and Design Patterns and Beer at Brightcove Group »

Bindable Getters and Getting Faster

We had a "bring your questions" session at our last Boston Flash User Group meeting at Brightcove, and I thought three of the items I answered would be helpful for more people to read.

Bindable Getters: If you're creating a component for someone else to use, you don't have to expose a setter for a bindable property. It's not very obvious that you can just expose a getter from the documentation, but it's simple to do. When you set up the getter, name the event for the Bindable property:

[Bindable(event="changeMyProp")]
public function get myProp():String {
  return _myProp;
}

Then when you set the value for the property, dispatch the named event. That's it:

_myProp = "my value";
// dispatch the event so that bound listeners know about the change
dispatchEvent(new Event("changeMyProp"));

Public MXML: Also on the subject of creating components, it was asked whether one should choose MXML or ActionScript for a class that's being used as a component. In general, I suggest to use ActionScript. This is because of all the public variables that are exposed when you use MXML, since all of the UI elements set up in the XML are public. This violates the open/closed principle and can be a problem for components that are used outside of a small project.

Fast and Stable: If you've been unsure if you can use the Faster
Flex SDK
for your own project, it should make you feel better that it's being used to build the Brightcove players that run on the New Yorks Times and many other major websites. So if you're concerned about the stability of the changes, I wouldn't worry about it. It's also being used at Brightcove to build some very large Flex manager applications without any problems.

Comments (10)

Good stuff! Concerning the Bindable Getters, you could also do the following to create a read-only bindable public property:

private var _myProp:String;

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

private function set myProp(value:String):void
{
_myProp = value;
}

And then just make sure you use myProp instead of _myProp throughout the code so that the propertyChange event gets fired.

I had the need to do that recently and found the suggestion at http://adamflater.blogspot.com/2007/08/binding-with-getters-and-setting.html

Thanks for the info Jamie- I didn't know this other approach worked as well.

Tink:

I would always advise specifying the event when declaring something as bindable. This means that only items bound to that particular property of the class will get updated.

If you use the default 'propertyChange', and you have 8 bindable properties, all the bindings would try to update, even though only 1 may have changed.

@Jamie McDaniel: I could be missing something but as soon as you assign a setter, it's not really a read-only property anymore is it? If you're not really manipulating anything in the getter/setter you may as well declare the property public and put the [Bindable] metatag in front of that.

@Jason Langdon: It is read-only outside the class. So in the example, the myProp property can only be read -- not set -- outside of the class. If you were to declare myProp as public, it could be set both inside and outside the class. If you were to declare it private, then it would be limited to just the class. We need it to be read by both, but limit the setting to just the class. (And then there is the need to support binding, which is simply an event being fired every time the property is set.)

A clearer example might be a class called ContactForm with a property called isValid. You would need such a property to be read-only. The instance of the class, say myContactForm, would need to be able to set isValid. The only way to accomplish this is to have a getter, but no setter (like Brian showed) or make the getter public and the setter private.

Now say you needed to do this:


Then you would need to support binding. So to allow for binding, you would need to dispatch an event everytime the class set isValid. Or you could use the method I did, which basically does the same thing once the Flex compiler sees the [Bindable] metadata tag and rewrites the code behind the scenes. It just saves you from having to manually dispatch the event every time you were to set the private property _isValid.

For a really great seminar on the inner workings of binding in Flex, go to http://tv.adobe.com/#vi+f15645v1029

I had to watch that video three times, though!

Sorry if I over explained this. I thought maybe someone would land on this post from a google search in the future. :-)

Oops, my mxml code got cut out from the post above. Here is the correction.

-----------------

Now say you needed to do this:

<view:ContactForm id="myContactForm" />
<mx:Button label="Save Contact" enabled="{myContactForm.isValid}" click="saveContact()" />

Then you would need to support binding.

-----------------

Follow-up: It seems that Brian's method (the original post) is best because of bug 174646.

-----------------

174646: If a class contains accessor functions with different access control namespace attributes, (for example, a protected setter and a public getter) using one of them causes a compile-time-error, for example,Compiler-Error 1000: Ambiguous reference to myVar

The workaround is to rename your getter or setter function to avoid the mismatch.

-----------------

I had not seen this compile-time-error before, but it showed up today using Flex SDK 3.3.

Here is a link to more info:

http://www.rubenswieringa.com/blog/ambiguous-reference-bug-for-namespaces-in-flex

Lots of great info Jamie, thanks!

How come the below will not work in a pure actionscript project whereas the alternative method with just writing [Bindable] above getter will work:

package
{
import flash.display.Sprite;
import flash.events.EventDispatcher;
import flash.events.Event;

public class BindableTest extends Sprite
{
private var _maxFontSize:Number = 15;

public function BindableTest() {
this.maxFontSize = 100;
}

[Bindable(event="maxFontSizeChanged")]
public function get maxFontSize():Number {
trace(" max ");
return _maxFontSize;
}

public function set maxFontSize(value:Number):void {
_maxFontSize = value;
var eventObj:Event = new Event("maxFontSizeChanged");
dispatchEvent(eventObj);
}

}
}

Thomas, I'm not sure. I'm surprised to hear that doesn't work in straight AS3. I would ask on the flexcoders mailing list.