React to change on a static property - apache-flex

I'm re-writing an MXML item renderer in pure AS. A problem I can't seem to get past is how to have each item renderer react to a change on a static property on the item renderer class. In the MXML version, I have the following binding set up on the item renderer:
instanceProperty={callInstanceFunction(ItemRenderer.staticProperty)}
What would be the equivalent way of setting this up in AS (using BindingUtils, I assume)?
UPDATE:
So I thought the following wasn't working, but it appears as if Flex is suppressing errors thrown in the instanceFunction, making it appear as if the binding itself is bad.
BindingUtils.bindSetter(instanceFunction, ItemRenderer, "staticProperty");
However, when instanceFunction is called, already initialized variables on the given instance are all null, which was the cause of the errors referenced above. Any ideas why this is?

You have 2 options that I am aware of:
Option 1
You can dig into the code that the flex compiler builds based on your MXML to see how it handles binding to static properties. There is a compiler directive called -keep-generated-actionscript that will cause generated files to stick around. Sleuthing through these can give you an idea what happens. This option will involve instantiating Binding objects and StaticPropertyWatcher objects.
Option 2
There is staticEventDispatcher object that gets added at build time to classes containing static variables see this post http://thecomcor.blogspot.com/2008/07/adobe-flex-undocumented-buildin.html. According to the post, this object only gets added based on the presence of static variables and not getter functions.
Example of Option 2
Say we have a class named MyClassContainingStaticVariable with a static variable named MyStaticVariable and another variable someobject.somearrayproperty that we want to get updated whenever MyStaticVariable changes.
Class(MyClassContainingStaticVariable).staticEventDispatcher.addEventListener(
PropertyChangeEvent.PROPERTY_CHANGE,
function(event:PropertyChangeEvent):void
{
if(event.property == "MyStaticVariable")
{
someobject.somearrayproperty = event.newValue as Array;
}
});

I think you need to respond to the "PropertyChanged" event.

If you're going to do that, use a singleton instead of static. I don't think it will work on a static. (If you have to do it that way at all, there are probably a couple ways you could reapproach this that would be better).
var instance:ItemRenderer = ItemRenderer.getInstance();
BindingUtils.bindProperty(this, "myProperty", instance, "theirProperty");

After fiddling with this for a while, I have concluded that this currently isn't possible in ActionScript, not even with bindSetter. It seems there are some MXML-only features of data bindings judging by the following excerpt from the Adobe docs (though isn't it all compiled to AS code anyways)?
You cannot include functions or array
elements in property chains in a data
binding expression defined by the
bindProperty() or bindSetter() method.
For more information on property
chains, see Working with bindable
property chains.
Source: http://livedocs.adobe.com/flex/3/html/help.html?content=databinding_7.html

You can create a HostProxy class to stand in for the funciton call. Sort of like a HostFunctionProxy class which extends from proxy, and has a getProperty("functionInvokeStringWithParameters") which will invoke the function remotely from the host, and dispatch a "change" event to trigger the binding in typical [Bindable("change")] Proxy class.
You than let the HostProxy class act as the host, and use the property to remotely trigger the function call. Of course, it'd be cooler to have some TypeHelperUtil to allow converting raw string values to serialized type values at runtime for method parameters (splitted by commas usually).
Example:
eg.
var standInHost:Object = new HostFunctionProxy(someModelClassWithMethod, "theMethodToCall(20,11)");
// With BindingUtils.....
// bind host: standInHost
// bind property: "theMethodToCall(20,11)"
Of course, you nee to create such a utlity to help support such functionality beyond the basic Flex prescription. It seems many of such (more advanced) Flex bindings are usually done at compile time, but now you have to create code to do this at runtime in a completely cross-platform Actionscript manner without relying on the Flex framework.

Related

Flex: Additional label in ButtonBarButton

I'm trying to extend ButtonBarButton to include an additional label that shows the number of updates for the respective box in a viewstack (which is the tabbar's dataProvider).
I can get the additional label (named indicatorLabel) to read an initial value from data, but I can't get it to update automatically like the actual "label" attribute. My understanding was that you could bind to the data object, but that doesn't appear to be the case.
The box that is used in the viewstack has an attribute called _indicator
[Bindable]
public var _indicator:String;
Which I know is updating properly because I can see it updating in the box (which also has a label bound to it). It appears to just not update the buttonbarbutton.
My buttonbarbutton class has the following (where init() is called in creationComplete
[SkinPart]
public var indicatorLabel:spark.components.Label;
private function init():void
{
indicatorLabel = data._indicator;
addEventListener("dataChange", onDataChangeHandler);
}
private function onDataChangeHandler(e:Event):void
{
trace(e.target.label + ' ' + e.target._indicator);
}
I'm guessing my assumptions for either databinding or the data obj that gets passed to the button are incorrect. Any help is appreciated.
I have answered a question here just a few days ago that is closely related to this one. It's a bit more abstract, but it should answer yours too. Have a look at Flex : Communicate between the skin and the data model?
As for your additional question about performance improvement: you just have to keep in mind that whenever you use binding, the compiler will generate some event listeners to listen for changes in the data. Of course that's not an issue with a one-off component or even with a bunch of instances, but it may become one if you have a List with thousands of components that all use binding. First of all the garbage collector won't clean these components up so easily when they're no longer needed and secondly - depending on your implementation - thousands of events may be firing at once.
bottom line: you could consider the commitProperties() approach performance tuning, hence you shouldn't consider it until you actually run into a performance issue.
on the other hand from an architectural point of view: that approach allows for a cleaner separation of the component and its skin. With the hostComponent / binding approach you could say the skin knows too much: it has to know about its host component and its properties. While the other one allows you to have a completely 'dumb' skin. So again I tend to use binding with one-off components and commitProperties() with highly reusable ones.
In the end it's all a trade-off (because the 'clean' way is more complex and more work) and it's up to you to make a weighed decision for each particular situation.

Extending Flex FileReference class to contain another property

I want to extend the FileReference class of Flex to contain a custom property. I want to do this because AS3 doesn't let me pass arguments to functions through event listeners, which makes me feel sad, so I need this property to exist on the event target, so I can access it.
I also want to be able to cast extant FileReference objects to this class without any fuss. I have:
var fr:SmxFR = e.target as SmxFR
and I want that to work; right now it just returns null.
A blank, newly instantiated SmxFR object has the extended property in place, but all of its inherited properties and objects return Error: Error #2037: Functions called in incorrect sequence, or earlier call was unsuccessful.
This is the class I am using, SmxFR.as:
package
{
import flash.net.FileReference;
public class SmxFR extends FileReference
{
public var housenum:String = "";
public function SmxFR()
{
super();
}
}
}
Kept it as straightforward as I could, really. Can someone please help me figure this out? Thanks.
Edit:
Per request, this is the instantiation which results in the aforementioned error in all inherited objects:
var fr:SmxFR = new SmxFR();
I get living handle property from that, and all other (that is, inherited) properties throw Error #2037.
So, maybe what I want to do is going to require overriding FileReferenceList? If the original objects must be instantiated to SxmFR, that's what I'll have to do, since I'm using FRL to allow the user to select multiple files at once. Are you guys sure there is no way to fast from a FileReference to my class?
You can totally pass objects via event listeners, it's just done in a specific way. I'd learn to do it correctly, rather than trying to extend a core library which could cause you problems later if you make a small mistake.
My solution: instead of extending FileReference, extend Event and add your properties to that.
var myEvent:MyExtendedEvent = new MyExtendedEvent();
myEvent.myCustomProperty = myValue;
dispatchEvent(myEvent);
Then in your handler you just write:
function myEventHandler(e:MyExtendedEvent):void {
trace(e.myCustomProperty);
}
Much more painless to go down this road! The added benefit is that if any other Flash Developer anywhere ever looks at your code they're not going to get hit in the face with a non-standard customized FileReference. :)
When e.target is instantiate as FileReference you can't cast it to SmxFR because it's not in the line of inheritance. In the other way you can a SmxFR Object to FileRefernce.
Extending FileReferenceList is not going to be helpful. FileReferenceList.browse() method creates an array of FileReference object when user selects multiple files - that happens internally (may be in its private methods) and you cannot change that behavior and force it to create SxmFR objects instead. Use custom events as Myk suggested.
This article talks about Sound objects, but may be that's applicable to FileReference objects too. May be you cannot reuse them. Post the code where you use the SmxFr class and get the said error.

Getting handles to dynamically-generated Flex components

I have a Flex application which references a separate MXML file as a template for a custom component. I create instances of the component dynamically several times in my program, but I need to get a handle that will allow me to modify that instance of the component as desired.
I pass specific information to this component on instantiation using bindable public variables in the component's MXML file. I add it to my main program using addChild().
I want to update the component's progressbar as necessary and I want to remove it from the box to which I addChild'd it.
What's the easiest/best way to get a variable that will give me predictable access to each component so I can easily manipulate the components as necessary? Some research suggests creationComplete, but I decided it was faster to just ask than to go through lots of different experiments and come up blank.
Thanks for all the help. : )
Can you not just keep a list of your components in an array? Presumably you have an object reference when you create them and call addChild() on their parent. Why not just put them in an array at the same time?
var list_of_controls:Array = new Array();
var new_Object:<yourType>;
new_Object = new <yourType>();
parent.addChild(new_Object);
list_of_controls.push(new_Object);
then you can get at them...
var my_Object:<yourType>;
for each (my_Object in list_of_controls)
{
// do something
}
You would have to make sure you dispose of them properly when you re done because the reference in your array would keep them in existence until cleared.
If you decide that you want to use getChildren() instead - which you could - take the time to read the documentation because I think it returns a new array with each call.
I hope that helps.

Can an Actionscript component listen to its own propertyChange events?

I have a CircleButton class in Actionscript.
I want to know when someone externally has changed the 'on' property.
I try listening to 'onChange' but it never hits that event handler.
I know I can write the 'on' property as a get/setter but I like the simplicity of just using [Bindable]
Can an object not listen to its own events?
public class CircleButton extends UIComponent
{
[Bindable]
public var on:Boolean;
public function CircleButton()
{
this.width = 20;
this.height = 20;
graphics.beginFill(0xff6600, 1);
graphics.drawCircle(width/2, height/2, width/2);
graphics.endFill();
this.addEventListener(MouseEvent.ROLL_OVER, rollover);
this.addEventListener(MouseEvent.ROLL_OUT, rollout);
this.addEventListener('onChange', onOnChange);
}
private function onOnChange(event:PropertyChangeEvent):void {
If you use the [Bindable] tag without specifying an event type, then when the property changes its value, an event of type: PropertyChangeEvent.PROPERTY_CHANGE, which is the string 'propertyChange', will be dispatched.
Therefore, to be able to register to listen to that event, you need to say:
this.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, onOnChange);
The reason why your listener function was never called is that the event type was not correct.
Note that the listener method will be called when any of the variables marked as Bindable in your class changes, not only 'on'. This event comes with a property called 'property' that indicates which variable was changed.
To avoid being called on each Bindable variable, you need to specify an event in the [Bindable] tag:
[Bindable(event="myOnChangeEvent")]
and dispatch that event manually when you consider that the property is changing (ie: in the setter), though that didn't seem to be what you wanted to do.
You could use BindingUtils.bindSetter()
An example is found here.
Just because it is possible for something to bind to the variable, doesn't mean something is bound to the variable. It's a bit like the event system - just because something can dispatch an event doesn't mean anything is listening.
The Classes around which the Flex binding is based are BindingUtils and ChangeWatcher. When you bind directly in MXML (which just gets converted to AS3 by the compiler) it uses these classes behind the scene to actually establish the binding. I've dug around in ChangeWatcher before and when it looks through the list of potentially bindable items it only dispatches if some object is actually listed as a listener. The whole binding system is a smart wrapper around the event system actually.
The semantics of binding in AS3 instead of MXML are different. Subtle differences (like chaining to child properties of Objects) that just work in MXML require work in AS3 to duplicate the behaviour (probably a result of the code generation between MXML to AS3).
Have a look at this Adobe doc for a little info on ChangeWatcher in AS code.
Personally - I do not use binding outside of MXML since I feel it is clumsy. I would suggest you write a setter function instead since it is more predictable (and very likely performant). I also suggest you read through the source code for ChangeWatcher and BindingUtils. That is definitely some of the most advanced AS3 you are likely to read.
One of my favorite approaches is the Observe class which is found here. It is essentially using a setter but it is a nice repeatable approach.

Flex: does painless programmatic data binding exist?

I've only done a bit of Flex development thus far, but I've preferred the approach of creating controls programmatically over mxml files, because (and please, correct me if I'm wrong!) I've gathered that you can't have it both ways -- that is to say, have the class functionality in a separate ActionScript class file but have the contained elements declared in mxml.
There doesn't seem to be much of a difference productivity-wise, but doing data binding programmatically seems somewhat less than trivial. I took a look at how the mxml compiler transforms the data binding expressions. The result is a bunch of generated callbacks and a lot more lines than in the mxml representation. So here's the question: is there a way to do data binding programmatically that doesn't involve a world of hurt?
Don't be afraid of MXML. It's great for laying out views. If you write your own reusable components then writing them in ActionScript may sometimes give you a little more control, but for non-reusable views MXML is much better. It's more terse, bindings are extemely easy to set up, etc.
However, bindings in pure ActionScript need not be that much of a pain. It will never be as simple as in MXML where a lot of things are done for you, but it can be done with not too much effort.
What you have is BindingUtils and it's methods bindSetter and bindProperty. I almost always use the former, since I usually want to do some work, or call invalidateProperties when values change, I almost never just want to set a property.
What you need to know is that these two return an object of the type ChangeWatcher, if you want to remove the binding for some reason, you have to hold on to this object. This is what makes manual bindings in ActionScript a little less convenient than those in MXML.
Let's start with a simple example:
BindingUtils.bindSetter(nameChanged, selectedEmployee, "name");
This sets up a binding that will call the method nameChanged when the name property on the object in the variable selectedEmployee changes. The nameChanged method will recieve the new value of the name property as an argument, so it should look like this:
private function nameChanged( newName : String ) : void
The problem with this simple example is that once you have set up this binding it will fire each time the property of the specified object changes. The value of the variable selectedEmployee may change, but the binding is still set up for the object that the variable pointed to before.
There are two ways to solve this: either to keep the ChangeWatcher returned by BindingUtils.bindSetter around and call unwatch on it when you want to remove the binding (and then setting up a new binding instead), or bind to yourself. I'll show you the first option first, and then explain what I mean by binding to yourself.
The currentEmployee could be made into a getter/setter pair and implemented like this (only showing the setter):
public function set currentEmployee( employee : Employee ) : void {
if ( _currentEmployee != employee ) {
if ( _currentEmployee != null ) {
currentEmployeeNameCW.unwatch();
}
_currentEmployee = employee;
if ( _currentEmployee != null ) {
currentEmployeeNameCW = BindingUtils.bindSetter(currentEmployeeNameChanged, _currentEmployee, "name");
}
}
}
What happens is that when the currentEmployee property is set it looks to see if there was a previous value, and if so removes the binding for that object (currentEmployeeNameCW.unwatch()), then it sets the private variable, and unless the new value was null sets up a new binding for the name property. Most importantly it saves the ChangeWatcher returned by the binding call.
This is a basic binding pattern and I think it works fine. There is, however, a trick that can be used to make it a bit simpler. You can bind to yourself instead. Instead of setting up and removing bindings each time the currentEmployee property changes you can have the binding system do it for you. In your creationComplete handler (or constructor or at least some time early) you can set up a binding like so:
BindingUtils.bindSetter(currentEmployeeNameChanged, this, ["currentEmployee", "name"]);
This sets up a binding not only to the currentEmployee property on this, but also to the name property on this object. So anytime either changes the method currentEmployeeNameChanged will be called. There's no need to save the ChangeWatcher because the binding will never have to be removed.
The second solution works in many cases, but I've found that the first one is sometimes necessary, especially when working with bindings in non-view classes (since this has to be an event dispatcher and the currentEmployee has to be bindable for it to work).
It exists as of today. :)
I just released my ActionScript data binding project as open source: http://code.google.com/p/bindage-tools
BindageTools is an alternative to BindingUtils (see the play on words there?) that uses a fluent API where you declare your data bindings in a pipeline style:
Bind.fromProperty(person, "firstName")
.toProperty(firstNameInput, "text");
Two-way bindings:
Bind.twoWay(
Bind.fromProperty(person, "firstName"),
Bind.fromProperty(firstNameInput, "text"));
Explicit data conversion and validation:
Bind.twoWay(
Bind.fromProperty(person, "age")
.convert(valueToString()),
Bind.fromProperty(ageInput, "text")
.validate(isNumeric()) // (Hamcrest-as3 matcher)
.convert(toNumber()));
Etc. There are lots more examples on the site. There's lots of other features too-come have a look. --Matthew
Edit: updated APIs
One way to separate the MXML and ActionScript for a component into separate files is by doing something similar to the ASP.Net 1.x code behind model. In this model the declarative part (the MXML in this case) is a subclass of the imperative part (the ActionScript). So I might declare the code behind for a class like this:
package CustomComponents
{
import mx.containers.*;
import mx.controls.*;
import flash.events.Event;
public class MyCanvasCode extends Canvas
{
public var myLabel : Label;
protected function onInitialize(event : Event):void
{
MyLabel.text = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit.";
}
}
}
...and the markup like this:
<?xml version="1.0" encoding="utf-8"?>
<MyCanvasCode xmlns="CustomComponents.*"
xmlns:mx="http://www.adobe.com/2006/mxml"
initialize="onInitialize(event)">
<mx:Label id="myLabel"/>
</MyCanvasCode>
As you can see from this example, a disadvatage of this approach is that you have to declare controls like myLabel in both files.
there is a way that I usually use to use mxml and action script together: All my mxml components inherit from a action script class where I add the more complex code. Then you can refer to event listeners implemented in this class in the mxml file.
Regards,
Ruth

Resources