Archive for December, 2009
Creating read-only bindable properties in Flex.
There may be times when you use an implicit getter/setter to take advantage of data-binding, but you'd prefer the property to remain read-only to the outside world.
When trying to do this many people initially attempt the following:
[Bindable]
public function get someValue():int
{
return _someValue;
}
private function set someValue(value:int):void //<-- 'private' access modifier
{
_someValue = value;
}
Note the use of the 'private' access modifier on the setter. Unfortunately this can lead to an "Ambiguous Reference" error at compile time (if you try to set the value in your code) and is not the correct way to approach the issue.
Other people attempt to create the getter method only (marking it as 'bindable'), but don't create the corresponding setter method. However, this results in error at compile time if you try to set the value ("Attempted access of inaccessible property") or a warning at compile-time: "A bindable tag on a read-only getter is unnecessary and will be ignored". Don't bury your head in the sand and ignore this warning - your binding may initially appear to work because Flex retrieves all the values at application start-up, but subsequent changes to the value will not be reflected.
Recap - [Bindable] & What Happens Behind Scenes
Let's quickly go over what is happening behind the scenes when we use the [Bindable] meta tag.
You may have seen the Bindable tag written several different ways:
- [Bindable] //default
- [Bindable (event="someEventType")] //specifying an event type
- [Bindable ("someEventType")] //short-hand way of specifying an event type
When we use [Bindable] with no event argument specified, certain code is generated for us behind the scenes at compile-time. Some of that code ensures that an event called "propertyChange" gets dispatched for us when our bindable value has been set. Although we just write [Bindable], Flex interprets that as [Bindable (event="propertyChange")] and once our value has been set, "propertyChange" (represented by the static constant PROPERTY_CHANGE on the PropertyChangeEvent class) is also dispatched for us.
Essentially this is what is happening:
[Bindable (event="propertyChange")]
public function get someValue():int
{
return _someValue;
}
public function set someValue(value:int):void
{
if(value != _someValue)
{
_someValue = value;
dispatchEvent(new PropertyChangeEvent(PropertyChangeEvent.PROPERTY_CHANGE));
}
}
It's worth noting that the auto-generated code also performs a check, only updating the value if the value being set is different to the current value (see One Reason Why Your Flex Getter May Not Execute)
So, when you use [Bindable], Flex is guaranteeing that it will be responsible for dispatching the "propertyChange" event for you, and to do this it needs the corresponding setter method. If you don't provide a setter, it doesn't have a opportune point at which to dispatch the "propertyChange" event for you. It also throws up the warning "A bindable tag on a read-only getter is unnecessary and will be ignored". This error seems to suggest that you may be wrong in wanting a private setter, but really it's a poorly worded error-string and what it's trying to say is that if you're relying upon the default [Bindable] behaviour (ie where "propertyChange" being dispatched) you're going to need a public setter method too.
Declaring An Event
As soon as we specifically declare an alternative event type, we break the contract with Flex , and in doing so become fully responsible for dispatching our own event. Flex doesn't care where we dispatch the event from. It could be from inside a setter, but it doesn't have to be. If we don't dispatch any event at all Flex won't complain - our code simply won't update as expected. Let's take a lok at specifying our own eventType and dispatching it ourselves:
private var _someValue:int;
[Bindable (event="someValueChange")]
public function get someValue():int
{
return _someValue;
}
private function generateRandomValue ():void
{
_someValue = Math.round(Math.random() * 100);
dispatchEvent(new Event("someValueChange"));
}
Although we only have an implicit getter method (no corresponding setter), we no-longer get a compiler warning. Our implicit getter looks like a public property, but because it has no setter it can't be set. _someValue is private and so that can't be set from outside the class either.
In order for binding to work, we need to send out a notification when the value changes, so that anything bound to it can call the getter and retrieve the updated value - we fulfil this obligation by associating our getter with "someValueChange", and then dispatching an event with "someValueChanged" as our eventType when our value changes - the difference is, we're doing this from a method in our code (in this case, a private method) and not from a corresponding setter.
Again, the thing you must remember is that, when you specify [Bindable(event="") you *must* dispatch the event yourself, even if you are creating a public getter/setter. Whatsmore, it's recommended to avoid the default for performance reasons - if multiple properties all dispatch "propertyChange" when their value changes, imagine the extra work your app has to do to work out which property-change caused the event to be dispatched.
Note: In the last example, I used flash.events.Event with an event type of "someValueChange" but we could have dispatched any subclass of Event (as long as the event type was "someValueChange"). As always, the examples shown are kept small and concise and not intended to indicate best practice.
6 comments