Binding read-only accessors in Flex
by Ruben
When I first tried to bind a read-only getter-function (see example-code below) the Flex SDK decided to warn me that "[Bindable] on read-only getter is unnecessary and will be ignored.".
private var _someProperty:String = "some value"; [Bindable] public function get someProperty ():String { return _someProperty; } // generates compile-time warning saying "[Bindable] on read-only getter is unnecessary and will be ignored." // note that there is no setter-functio
Furthmore, the LiveDocs said the following about the binding getter-only accessors:
If you define just a getter method, you create a read-only property that you can use as the source of a data-binding expression without inserting the [Bindable] metadata tag. This is similar to the way that you can use a variable, defined by using the const keyword, as the source for a data binding expression.
Bindable metadata tag (Adobe Flex LiveDocs)
This is, of course, complete and total bogus. Having a getter-method that is not accompanied by a corresponding setter-method does not necessarily mean that the method its returned value will not ever change, eliminating the option of using the const keyword.
So I had a quick read through the Flex LiveDocs and came up with the following solution:
// the inner internal: private var __someProperty:String = "some value"; // the public property (read-only): [Bindable(event='somePropertyChanged')] public function get someProperty ():String { return _someProperty; } // the internal (read/write): private function get _someProperty ():String { return __someProperty; } private function set _someProperty (value:String ):void { __someProperty = value; dispatchEvent(new Event("somePropertyChanged")); }
As you can see, where the someProperty public getter is made bindable, I used the event attribute of the [Bindable] metadata-tag. You can use the event attribute to assign an Event that will trigger the bindings with a certain member to be refreshed.
I assigned 'somePropertyChanged' as the Event that would refresh the bindings with someProperty.
I also turned the original internal ( _someProperty) into a getter/setter, and created an inner internal for the the internal getter/setter ( __someProperty, two underscores).
Now when the internal setter method ( _someProperty) is called, it automatically dispatches an Event typed 'somePropertyChanged', and thus indirectly triggers the bindings to the public accessor (someProperty) to be refreshed..
This is the cleanest and simplest way of doing this that I could come up with.
However, as far as the databinding mechanism is concerned, keep in mind that only explicitely resetting the value of a variable (for example myVar='new value') counts as change.
Executing a method of a certain variable (for instance myArray.push('new value')) does not count as change, and adjusting the value of an element of a variable (for instance an Array or Object) does not either.
I am currently still trying to find out what the best way is to handle databinding with Arrays or Objects, as soon as I figure it out I'll be sure to put together a follow-up post on this topic.
NOTE: Although, for the sake of clarity, I used the private attribute in the above code-examples, I strongly advise to use the protected in favor of private in almost any case.
This is because a subclass does have access to its superclass' protected properties, whereas it does not have access to its superclass' private properties.
Comments (read newer or show 9 trackbacks)
Trackbacks:
Cool work-around, but what a hassle to get around a ‘rule’ Flex is enforcing.
Yea tell me about it man. What strikes me most is the theory they use to back up their choice of not allowing bindings on getter-only accessors; namely that you should use constants for read-only properties..
Well whatever, hopefully someone from the Flex enginering team bumps into this post and corrects this issue..
Hi guys. what you’re bumping up against is a problem with the error string, not with the theory. There are two ways you can make a get/set property bindable.
first, you can dispatch your own custom event when the value (computed or variable backed) you would read if you accessed the get function changed. In this case you need to inform the compiler that the property change is signified by your own custom event using the [Bindable("eventName")] syntax.
Second, in simple cases, you can simply put [Bindable] on the property, with no event specified. In this case, you’re asking the compiler to make the get/set property bindable for you.
Now keep in mind that there’s no way for the compiler to actually tell if the value of a property get function would be different if called, short of doing an extensive code flow analysis of the get function, identifying all the inputs that might be affecting the value of the get function (i.e., member fields, statics, globals that are used in the get function and in any methods, global functions, closures, etc) it might call, and setting up watchers on every one of those to trigger the binding when any of them change. That’s prohibitively difficult, and expensive to do. So the compiler doesn’t try.
Instead when you put [Bindable] on a get/set property, the compiler makes it bindable with a little creative rewriting that allows the framework to watch the get function, and dispatch a change event when the get function is triggered. This means that automatic bindable properties don’t work when the get function is computed from multiple values, or when you change its value by setting a backing field, rather than using the set function.
It _also_ means that if you have no set function, we can pretty much guarantee that there’s no way automatically bindable get properties will be triggered. a read only propeerty is, to the compiler, completely opaque…at the moment, it has no idea where that value is coming from, and hence will never be able to ‘automatically’ trigger the binding.
So what the compiler is saying in this case is not that you can’t make read only properties bindable, but that specifically the [Bindable] metadata, in that form, with no event specified, will have no effect. the error message could probably use a little bit more clarity.
Ely Greenfield.
Adobe, Flex SDK.
“Having a getter-method that is not accompanied by a corresponding setter-method does not necessarily mean that the method its returned value will not ever change…”
You are correct. The value of a getter may change at any time.
You’re responding to the following statement in the documentation:
“This is similar to the way that you can use a variable, defined by using the const keyword, as the source for a data binding expression.”
Notice that they say “similar”. As far as I can tell, they are not implying that the value of your getter will never change. I believe they mention constants because constants don’t need [Bindable] either.
That is indeed pretty clever. However, isn’t the other side of the token that properties declared as bindable can also act as the destination of a binding change? If BindingUtils.bindProperty(this, “dataprovider”, model, “someProperty”); works, what happens if i change it to BindingUtils.bindProperty(model, “someProperty”, this, “dataprovider”);? I think the prerequisite for all bindable properties to be both read and write ensures compatibility with data-binding, considering that the use of strings to represent the properties don’t allow that level of strict-checking.
When you say databinding with arrays, don’t you mean like ArrayCollection?