Best Practise to Define and update MXML component in Flex application - apache-flex

A Flex components values are initialized by init method.
In an application flow,
How to refresh a mxml component data value ; as init is called at the start up itself only.
Example of mxml component may be
as simple as button label or text
as complex as repeater whose data provider is a web service
( means a fresh quesy should be made to pull the data and refresh the dataprovider of repeater )

If the dataprovider is a collection or an array it will update itself as items are added to or deleted from the collection. You can listen to the CollectionEvent.CollectionChange event to see when a collection changes.
I'm not really sure what you mean though? Are you on about binding?

If you want to re-init the whole control, you could create an "reset" event and have the handler for the reset execute the same behavior as the init code.
That's the best I can do without more details...

you should create yourself setters and getters for the properties you want to modify and a refresh is required afterwards. for example:
private var _tmp : String = '';
public function set tmp(val : String) : void {
this._tmp = val;
this.doOtherDataRefreshNeeded();
}
public function get tmp() : String {
return this._tmp;
}
and this way, everytime the code that uses this component and needs to update it's tmp property. the setter will be called and in there a lot of other stuff can happen beside assigning the value.
for simple mxml components as texts and inputs, use bindings {} for their data values. those should update as soon as the data changes. if not, call .invalidateNow() method on them to force update.

use ValidateNow() method in mxml component in updating method

Related

Getting ahold of the Application to call its method

In my Flex 4.5 application I have a TitleWindow Settings.mxml, which is popped up by the PopUpManager.
Once the user has changed some settings, I not only need to save them to a SharedObject, but also to apply them to the main Application itself - so that the changes are visible to the user immediately.
For example I need to call its method hideApp(somevalue);
The spark.components.Application does not seem to have any static/singleton methods to get ahold of it.
So how do you do it?
And I also wonder how to declare, that an MXML file implements one or several interfaces?
package {
public interface Hiddable {
function hideApp(value:Number):void;
}
}
I'm asking this, because besides the main Application I have a SettingsTest.mxml Application in my project for "unit testing" that particular functionality.
Thank you! Alex
Yes it does:
FlexGlobals.topLevelApplication
though I would recommend you use events to avoid tight coupling.
As for the question about interfaces: use the attribute implements
<s:Component ... implements="IClassA,IClassB" ... />
About implementing of interfaces in MXML components see the following documentation.
What about passing changed data back from your pop up window to the application I recommend you to use Observer pattern with Flash event model something like the following:
var myWindow:MyWindow = MyWindow(PopUpManager.createPopUp(this, MyWindow));
myWindow.addEventListener(MyWindowEvent.SUBMIT, myWindowSubmit);
private function myWindow(event:MyWindowEvent):void
{
// Unsubscribing from events
var myWindow:MyWindow = MyWindow(event.currentTarget);
myWindow.removeEventListener(MyWindowEvent.SUBMIT, myWindowSubmit);
// Changed data is passing with custom event object
someData = event.someData;
}
And you should implement your custom event for that (MyWindowEvent in my pseudo code) and fire it from your TitleWindow component. You can read more about implementing custom event in documentation.

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.

Can I add an event listener to a databinding action in Flex?

I have a ComboBox that I bind to a standard HTTPService, I would like to add an event listener so that I can run some code after the ComboBox is populated from the data provider.
How can I do this?
Flex doesn't have a specific data-binding events in the way that say ASP .Net does. You have to watch for the dataProvider property like John says in the first answer, but not simply to the combobox or its dataProvider property. Let's say you have a setup like this:
<!-- Assume you have extracted an XMLList out of the result
and attached it to the collection -->
<mx:HttpService id="svc" result="col.source = event.result.Project"/>
<mx:XMLListCollection id="col"/>
<mx:ComboBox id="cbProject" dataProvider="{col}"/>
Now if you set a changewatcher like this:
// Strategy 1
ChangeWatcher.watch(cbProject, "dataProvider", handler) ;
your handler will not get triggered when the data comes back. Why? Because the dataProvider itself didn't change - its underlying collection did. To trigger that, you have to do this:
// Strategy 2
ChangeWatcher.watch(cbProject, ["dataProvider", "source"], handler) ;
Now, when your collection has updated, your handler will get triggered. If you want to make it work using Strategy 1, don't set your dataProvider in MXML. Rather, handle the collectionChange event of your XMLListCollection and in AS, over-write the dataProvider of the ComboBox.
Are these exactly the same as a databound event? No, but I've used them and never had an issue. If you want to be absolutely sure your data has bound, just put a changeWatcher on the selectedItem property of your combobox and do your processing there. Just be prepared to have that event trigger multiple times and handle that appropriately.
You can use a mx.binding.utils.ChangeWatcher as described here.
You can use BindingUtils to get notified when the dataProvider property of the combo box changes:
BindingUtils.bindSetter(comboBoxDataProviderChanged, comboBox, "dataProvider");
BindingUtils lives in the mx.binding.utils package.
I have a longer description of how to work with BindingUtils here: Does painless programmatic data binding exist?
You can also listen for the ResultEvent.RESULT on the HTTPService, that would be called slightly before the combo box got populated I guess, but it might be good enough.
Where are you adding the listener compared to the loading of the data? Is it possible the data is being loaded, and the event fired, before you've added your listener?
#Herms
The listener is definitely added before the web service call, here is an example of what my code look like (I simplified lots of things...):
I have this flex component:
public class FooComboBox extends ComboBox
{
private var service:HTTPService = null;
public function ProjectAutoComplete()
{
service = new HTTPService();
service.url = Application.application.poxmlUrl;
service.addEventListener(FaultEvent.FAULT,serviceFault);
service.addEventListener(ResultEvent.RESULT,resultReturned);
this.addEventListener(FlexEvent.DATA_CHANGE,dataChange);
}
public function init():void
{
var postdata:Object = {};
postdata["key"] = "ProjectName";
postdata["accountId"] = Application.application.accountId
service.send(postdata);
}
private function resultReturned(event:ResultEvent):void
{
this.dataProvider = service.lastResult.Array.Element;
// thought I could do it here...but no luck...
}
private function dataChange(e:FlexEvent):void
{
// combobox has been databound
mx.controls.Alert.show("databound!");
}
...
}
and then in a mxml file I have the FooComboBox with id "foo" and I call:
foo.init();
I need to execute some code after the combobox is completely databound...any ideas?
Maybe the event doesn't trigger when the data provider is first set? Try setting the data provider to an empty array in the constructor, so that it's definitely changing instead of just being initially assigned later in your resultReturned() method. I've no clue if that will help, but it's worth a shot.
Also, you're setting the provider to lastResult.Array.Element. That looks a little suspicious to me, as the data provider should probably be an array. Granted, I have no clue what your data looks like, so what you have could very well be correct, but it's something I noticed that might be related. Maybe it should just be lastResult.Array?
In your example code, try running validateNow() in the resultReturned method. That will force the combo box to commit its properties. The thing is that even though the property is set the new value isn't used until commitProperties is run, which it will do at the earliest on the next frame, validateNow() forces it to be done at once.

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