Thursday, October 16, 2008

BindingUtils Example

I am working on an application which is not architect properly. So, I need to find solutions which just keeps things going. I have an illustration below for your reference:
image
Say, I have 3 custom components on my screen namely:
  • Component 1
  • Component 2
  • Component 3
Requirement is:
For Component 1:
Whenever some specific property changes in Component 1, it should be reflected in Component 3.
Whenever some properties in Component 2 changes, Component 1 should reflect those values.

For Component 2:
Nothing, It could just work independently

For Component 3:
It can work independently but it has to display updates from Component 1 and Component 2.
Problem:
As I mentioned there is no Framework used and application is not architect so I used Binding for rescue. (You can do it by passing values in a CustomEvent and passing those values from one component to other component, I guess BindingUtils do the same.)

Below is a sample code:
BindingUtilsExample.mxml
<?xml version="1.0" encoding="utf-8"?>


<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="onCC()">


<mx:Script>


<![CDATA[


import mx.binding.utils.BindingUtils;


import mx.controls.Alert;
[Bindable]
public var ds:DataSingleton = DataSingleton.getInstance();

/**
  * called on creation complete to bind my textinput to my data
  */
public function onCC():void{
BindingUtils.bindProperty(myTxtInput,"text", ds,"data");
}

/**
  * Every time I click on button, I update values in my model (Simply a +1 that too string
  */
public function onClick():void{
if(ds.data != null )
{
ds.data += " 1";
}else{
ds.data = "some value";
} 

//Alert.show(ds.data);
}
]]>
</mx:Script>
<mx:VBox height="100%" width="100%">
<mx:HBox width="100%" height="100%">
<mx:Panel title="Component 1" height="100%" width="100%">
<mx:TextInput id="myTxtInput" text="This will have binding from my model"  width="100%"/>
</mx:Panel>
</mx:HBox>
<mx:HBox width="100%" height="100%">
<mx:Panel title="Component 2" height="100%" width="100%">
<mx:Label text="Click on button to update model value"/>
<mx:Button label="Click me" click="onClick()" />
</mx:Panel>
</mx:HBox>
</mx:VBox>
</mx:Application>
DataSingleton.as
package {
public class DataSingleton {
/**
  * Single instance which will exist throughout the app
  * for this class
  */
private static var instance:DataSingleton;
/**
  * Flag to check wheather instance already exist or not
  */
private static var allowInstantiation:Boolean;
/**
  * Data which I want to share across components
  */
private var _data:String;
/**
  * Method to get the instance of this class
  */
public static function getInstance():DataSingleton {
if (instance == null) {
allowInstantiation = true;
instance = new DataSingleton();
allowInstantiation = false;
}
return instance;
}
/**
  * Constructor
  * Checks if instance already exists then throw an error
  */
public function DataSingleton():void {
if (!allowInstantiation) {
throw new Error("Error: Its a Singleton Class. Use DataSingleton.getInstance() to instantiate.");
}
}
[Bindable]
/**
  * Make getter method of your data [Bindable] to make BindingUtils to get these values
  */
public function get data():String{
return this._data;
}
/**
  * Setter for my data to share across
  * Call this method in one of your component to update it and let BindingUtils update it for you 
  * in some other component
  */
public function set data(value:String):void{
this._data = value;
}
}
}

Here is what I have done:
  1. Create a Singleton Class
  2. Defining the properties you want to bind and share across different Components
  3. Make the getter of that property [Bindable]
    e.g.:


    [Bindable]
    //Make getter method of your data [Bindable] to make BindingUtils to
    //get these value
      public function get data():String{
          return this._data;
      }

  4. On creationComplete event (this is specific to my case, may be you need to find when it is suitable for you to bind, as when all components get ready)

    BindingUtils.bindProperty(myTxtInput,"text", ds,"data");

    I have one text input in my example which needs to be updated on click of a Button in some other component. Click of button, basically updates property of my singleton class. Here is a description for arguments provided in bindProperty method:






    1. myTxtInput: Object which you want to update
    2. text: Object's property which will get the value (In my case its a textinput text property)
    3. ds: Object from where you which you will get the value
    4. data: property which needs to be assigned. (Right side argument)


One more finding (Setting dataProvider in actionscript 3):

In MXML, I used to write something like

public var myArrayCollection:ArrayCollection = new ArrayCollection();

<mx:SomeComponent id="myComponent" dataProvider={myArrayCollection} ..... />

To set dataProvider in actionscript and binding it, equivalent to above is:

BindingUtils.bindProperty(myComponent,"dataProvider", this,"myArrayCollection");

To see sample example Click here.

4 comments :

  1. I would advise to architect it properly and rewrite if you have to because if you hack it now you might have to do it again resulting in dirty code. It would take long but you will be proud of your achievements.

    ReplyDelete
  2. @andhapp
    Thanks for the suggestion andhapp. But sometimes you don't have the control on everything. If I am the owner of this project, I could have done that. :)

    ReplyDelete
  3. Nice Thought and solution..
    -dinesh

    ReplyDelete