Docstoc

Test Driven Development _TDD_

Document Sample
Test Driven Development _TDD_ Powered By Docstoc
					      Flex data binding pitfalls:
 10 common misuses and mistakes




@EladElrom
@EladElrom
•   Associate Dev Director @ Sigma Group
•   Senior Flash Engineer & Lead
•   Technical Writer
•   FlashAndTheCity Organizer
•   Adobe Community Professional
  DataBinding - old topic yet still relevant‎
Data binding—the process of passing the data in one object to another object
automatically—is one of the most used and useful features when building Flex and
Adobe AIR applications. At the same time, however, data binding can slow
application initialization and cause frustration when developers don't fully
understand how the mechanism works. It is a good idea to make sure you are using
it correctly and only when needed.
  TOC
Flex data binding pitfalls: common misuses and mistakes

1. Missing silent errors
2. Trying to bind a class that does not include the
   IPropertyChangeNotifier interface
3. Using binding in place of direct assignment
4. Forgetting to unbind and risking memory leaks
5. Not changing the default propertyChange event constant
6. Using the wrong bindable event name
7. Assuming an execution order for binding
8. Using binding in place of events
9. Binding a class and its properties
10. Using two-way data binding for unsupported properties

Lastly, Turbo Binding
Mistake #1: Missing silent errors
     Missing Silent Errors

There are cases where the binding operation just does not seem
to work, and you end up frustrated and unsure of what to do.

Exceptions and errors that are thrown by binding expressions, or
in binding functions called within the binding framework, are
silently captured. As a result, you will not see a runtime
exception as you might expect in the debug version of Flash
Player. Not only does the binding not work, but no errors are
shown.
 Why Are Errors being Silently
         Captured?

The code that implements the binding mechanism requires several
conditions to be met before the binding will occur. The binding mechanism
will swallow any errors to prevent runtime exceptions from being thrown at
runtime. This is a good thing since you do not want to see these (possibly)
unexpected errors in your application.
Binding Error Example
              I have added an xml variable
              binding to a Label component.
              The code would have worked fine;
              however, I have set the xml
              variable to null during the pre-
              initialization of the component.

              The event was dispatched at the
              beginning of the component
              initialization sequence, so the
              Label component was not set yet.
              The xml variable gets set to null,
              so there is no name property on
              the xml object. If you run this
              application, you'll notice that
              binding does not occur and the
              errors have been silently
              captured.
                    Debugging Binding
Although errors are captured silently,
there are still things you can do to
figure out what is going on.
Debugging with the
BindingManager.as and Binding.as
code is not easy since the binding
classes are not available to you unless
you download the entire Flex SDK.

Instead, you can set a break point
and drill down to the related binding
objects to find out what went wrong.
In this case, you would find that the
value of the XML object is set to null,
and that is why the binding has
failed.
Binding and BindingManager
Take a look at the Binding.as and BindingManager.as classes. The
code has many if and try… catch statements that ensure conditions
are met for performing a valid binding. Here are some of the error
cases that can be thrown when using binding:

  * Error #1006: Call attempted on an object that is not a function.
  * Error #1009: Null has no properties.
  * Error #1010: Undefined has no properties.
  * Error #1055: Has no properties.
  * Error #1069: Property - not found on - and there is no default
value

If any of these errors occur, the binding manager catches them
silently and will not perform the binding.
             Debugging Binding

Another approach, which is more intuitive, is to use the debugBinding
method in the BindingManager class. You set the component and
property you want to watch, and then you can see the errors that were
fired and silently captured.
          Null Errors Examples
Runtime errors can occur normally in many scenarios.

1.   a Label component is initialized that contains a data binding
     expression, the target property may not yet be set and execution of
     the binding expression will result in an Error #1009 – Null has no
     properties runtime exception.
2.   Similarly, some data binding expressions are only valid when the user
     of your application performs an action such as selecting an item in a
     List. If your data binding expression references the selectedItem
     property of the List, it will be null until the user actually clicks an
     item in the List and selects it.
                      Debugging Binding
In the example code I left the following line of code commented:


  BindingManager.debugBinding("label.text");

  Uncomment this line and run the application in
  debug mode; you can see the binding errors in
  the Console view.
Debugging Binding Console
       Mistake #2
Trying to bind a
class that does not include
the IPropertyChangeNotifier
interface
  IPropertyChangeNotifier Marker
Classes that implement the IPropertyChangeNotifier marker interface must dispatch
events for properties in the class, and any nested classes are publicly exposed as
properties. As a result, you can find out when properties have changed in the class.
For instance, take a look at the UIComponent class signature. UIComponent indeed
implements dispatchPropertyChangeEvent, which will dispatch an event once
properties have changed.
                               ValueObject
 Now consider the following class that holds
 user information:




If you try to bind the text property of a Label to one of the properties of the UserInfo
class, it will not work!
Reason
         Because the code is trying to
         bind a class that does not
         implement
         IPropertyChangeNotifier, the
         binding mechanism will not
         work.

         In this case, you'll see the
         following message in the
         Problems view (see Figure):
                                     Solution
attach the [Bindable] tag to the class. This will enable all public properties of the class
for data binding. The Flex compiler will generate a public getter and setter for you
which will contain all of the code necessary to make data binding work. Alternatively,
you can attach the [Bindable] tag to specific properties of the class if you don't want
to enable all properties for data binding.
                ObjectProxy Class
Data binding requires that the class to which you are binding implements
the IPropertyChangeNotifier interface. Otherwise, the object is not
bindable.

However, classes/properties or variables, such as primitive variables, that
are not marked [Bindable] do not implement that interface. If it is your own
class, all you have to do is add the [Bindable] metadata tag. If it's not your
class you wish to bind with, or you want to add binding functionality during
runtime you can use the ObjectProxy class. ObjectProxy wraps a non-
bindable class and dispatches a PropertyChangeEvent when any of the
properties of the class are changed, enabling objects in your application to
listen for property changes.
  Below is an example of using
   ObjectProxy. I create a new
  instance of ObjectProxy and
pass the object I want to watch,
in this case UserInfo. I then add
   an event listener and track
changes to an item in UserInfo.
       ObjectProxy caveat

When using ObjectProxy is that in order to facilitate assignment
notification, registered listeners will be invoked every time any
property on the target object changes. That can potentially
introduce a significant overhead, in fact in our example the
onPropertyChange is called twice since two changes occurred.
Mistake #3: Using binding in place
      of direct assignment
What’s wrong with the code below?
                        Answer
The code defines a TextInput control with a text property binding to
the text private variable. It looks harmless enough, right?

I have seen these types of tags often in Flex applications. The Flex
compiler generates code to allow the binding. You will find that
although you do not need to bind the text String since it is a one-time
assignment, the compiler still generates code to accommodate
binding of the property.

Additionally, there are cases where you want to unbind after the
assignment or remove the binding code to reduce overhead, but you
will not be able to do so using the <mx:Binding> tag in MXML.
                 “-keep” generated mxmlc code
class BindableProperty                                                                                                  package
{
             /*                                                                                                         {
              * generated bindable wrapper for property someTextInput (public)                                          [ExcludeClass]
              * - generated setter                                                                                      public class _TesterWatcherSetupUtil
              * - generated getter
              * - original public var 'someTextInput' moved to '_2040386569someTextInput'                                 implements mx.binding.IWatcherSetupUtil2
              */                                                                                                        {
                                                                                                                          public function _TesterWatcherSetupUtil()
    [Bindable(event="propertyChange")]
    public function get someTextInput():spark.components.TextInput                                                        {
    {                                                                                                                        super();
       return this._2040386569someTextInput;                                                                              }
    }

  public function set someTextInput(value:spark.components.TextInput):void                                                public static function init(fbs:IFlexModuleFactory):void
  {                                                                                                                       {
                var oldValue:Object = this._2040386569someTextInput;
    if (oldValue !== value)                                                                                                 import Tester;
    {                                                                                                                       (Tester).watcherSetupUtil = new _TesterWatcherSetupUtil();
        this._2040386569someTextInput = value;                                                                            }
       if (this.hasEventListener("propertyChange"))
           this.dispatchEvent(mx.events.PropertyChangeEvent.createUpdateEvent(this,
"someTextInput", oldValue, value));                                                                                       public function setup(target:Object,
    }                                                                                                                                    propertyGetter:Function,
  }
                                                                                                                                         staticPropertyGetter:Function,
               /*                                                                                                                        bindings:Array,
                * generated bindable wrapper for property someText (private)                                                             watchers:Array):void
                * - generated setter
                * - generated getter                                                                                      {
                * - original private var 'someText' moved to '_1504842817someText'
                */                                                                                                           // writeWatcher id=0 shouldWriteSelf=true
    [Bindable(event="propertyChange")]                                                                                  class=flex2.compiler.as3.binding.PropertyWatcher shouldWriteChildren=true
    private function get someText():String                                                                                   watchers[0] = new mx.binding.PropertyWatcher("someText",
    {                                                                                                                                                                    {
       return this._1504842817someText;
    }                                                                                                                               propertyChange: true
                                                                                                                                }
    private function set someText(value:String):void                                                                    ,
    {
                   var oldValue:Object = this._1504842817someText;                                                                                                    // writeWatcherListeners id=0 size=1
       if (oldValue !== value)                                                                                               [
       {                                                                                                                     bindings[0]
           this._1504842817someText = value;
          if (this.hasEventListener("propertyChange"))                                                                       ]
              this.dispatchEvent(mx.events.PropertyChangeEvent.createUpdateEvent(this, "someText", oldValue, value));   ,
       }                                                                                                                                                        propertyGetter
    }
                                                                                                                        );

                                                                                                                             // writeWatcherBottom id=0 shouldWriteSelf=true
}
                                                                                                                        class=flex2.compiler.as3.binding.PropertyWatcher
                                                                                                                             watchers[0].updateParent(target);
Rule of thumb

1. Avoid using binding to a private
   variables as much as possible.
2. Do not use data binding unless
   the property to which you are
   binding can or will change.
              Direct Assignment

In the example I showed you previously you can use direct assignment to
set the value:

<s:TextInput text="some text goes here" />

When you use direct assignment, you significantly reduce your overhead
because the compiler does not create unnecessary binding code.
              Mistake #4
Forgetting to unbind and risking memory leaks
                      BindingUtils
You can use the <mx:Binding> tag in MXML or curly
braces to easily implement binding; however, there is
overhead associated with these methods.

Additionally, you cannot unbind properties using these
techniques. If you are optimizing a high-performance
application, you can use the BindingUtils class to bind
your objects.

There are two ways to use the BindingUtils class:
  * The bindProperty() method is a static method used to bind a public
property.
  * The bindSetter() method is a static method used to bind a setter
function.
                       bindProperty
public static function bindProperty(
  site:Object, prop:String,
  host:Object, chain:Object,
  commitOnly:Boolean = false,
  useWeakReference:Boolean = false):ChangeWatcher

site = destination
host = source

You set commitOnly to true when the handler is to be called only
on committing change events; set it to false (the default) when the
handler is to be called on both committing and non-committing
change events.

A strong reference (the default) prevents the host from being
garbage-collected; a weak reference does not.
bindProperty Example
                The example includes
                a text input and a
                simple text
                component. When
                the TextInput control
                is preinitialized,
                preinitializeHandler()
                is called, which uses
                the bindProperty
                method.
                       bindSetter
Here is the bindSetter method signature:

public static function bindSetter(setter:Function, host:Object,
 chain:Object,
 commitOnly:Boolean = false,
 useWeakReference:Boolean = false):ChangeWatcher

The setter parameter specifies the setter method to invoke with an argument
of the current value of chain when that value changes. Here again, host
represents the source object, and chain represents the property name. The
commitOnly and useWeakReference parameters work as with bindProperty.
bindSetter Example
              Behind the scenes, the
              ChangeWatcher class is
              used in the BindingUtils
              class. It allows weak
              references, so when you set
              useWeakReference to true
              the host will automatically
              be picked up by the
              garbage collector, avoiding
              potential memory leaks.

              Default (just like event
              listeners) is false!
         Avoid Memory leak
When you don’t set the weak references to true, the
ChangeWatcher object needs to be unwatched after
you are finished with it. It is your responsibility to
handle that task, which will ensure there are no
memory leaks.

The best way to handle that is to assign the static
method returned by bindProperty to a ChangeWatcher
variable (since the static method returns a
ChangeWatcher instance you can assign it to a
variable). When you do not need to bind the object
anymore you can use the unwatch method
Example
          The TextInput text
          property is binding to the
          Label text property, and
          once you type text in the
          TextInput, the data will be
          copied to the text property
          in the Label component.
          When you are ready to
          unbind, click Stop Binding.

          This will unwatch the
          properties and set the
          object to null so it will be
          picked up during garbage
          collection.
Mistake #5
Not changing the default
propertyChange event constant
                     Default type
When you use the Bindable tag without adding an event setting,
propertyChange is the default event type that will be dispatched.

[Bindable] = Bindable(event="propertyChange")

The compiler creates additional code for the setter and getter
when you do not specify the event string.

It is recommended that you add your own name constant to
avoid this extra overhead.
        Compiler creates a lot of code




As you can see the mxmlc creates a generated setter which contains code that
dispatches the PropertyChangeEvent.

If you don't change the default constant, every [Bindable] property will dispatch the
propertyChange event and the listening code must interrogate the event to determine
exactly which property changed. This is especially costly process if a class has a large
number of [Bindable] properties.
               Do it yourself
Once you set the event name yourself, such as in the example
below, the compiler just copies the code over.

When you change the default constant, the Flex compiler will NOT generate code
and you are responsible for dispatching the event yourself. This allows you to
optimize the listening code.
  Mistake #6
Using the wrong bindable event name
    What’s wrong with this code?
Using the wrong event name in the [Bindable] tag can cause your
application to not bind your property, and you will not even know
why! When you use the [Bindable] tag with a custom name, the
example below looks like a good idea:
       Answer
The code above assigns a static property to the event name, and then uses the same
assignment to dispatch the event. However, when the value changes, the binding does
not appear to work. The reason is that the event name will be
EVENT_CHANGED_CONST and not the value of the variable.

Correct code:
Mistake #7
Assuming an execution order for binding
* This one is a very common one!
            Common Mistake

Assuming that binding occurs in a synchronous execution
order. This can cause your application to generate
warnings and not bind your property.

Events in ActionScript are executed in an asynchronous
manner.
Example
          The code above may work;
          however, it may not. It
          assumes that the Label text
          property was already set
          since the value of label.text
          in the button component is
          binding to the Label.text
          property. If you compile this
          application you'll also get a
          compile time warning.
Compile time error
                 Another Example
Below is another example. It assumes that the value of the first
TextInput control is already set. This type of assignment also causes the
compiler to generate all the code needed for binding. You have to
decide if that code is needed or if a direct assignment (y=35) is a better
choice.
Mistake #8

Using binding in place of events
                      Explanation
There are many cases where you can write your code easily without data
binding and instead use events to assign a value.

You can set a value using the appropriate component lifecycle event or
overriding component’s methods such as childrenCreated() or
initializationComplete() to do the a value assignment.

 Additionally, there are times when you can use ChangeWatcher to listen
to changes in data, which is less costly. When using ChangeWatcher keep
in mind that there are many cases where you can even avoid using the
ChangeWatcher since you can manually get notification.

For instance, all collections in Flex have a collectionChange event
broadcasted, which you can use to manually get change notification
within the collection object.
Example
          It looks like
          pretty standard
          code for a Flex
          application.
          However, the
          type of binding
          illustrated here
          is not needed.
Better way…
              use direct assignment in an
              event handler:
Mistake #9
Binding a class and its properties
                          Example
Another common mistake is to set a class to be bindable and then make each
property in the class bindable as well; for example:
               Compile time warnings
The [Bindable] tag is not needed for the CustomerID property because the class is
already marked as bindable, which makes every property in the class bindable. This
creates compile time warnings (see Figure) and writing the extra code wastes time.
Unless you specify an Event name the tag is redundant and should be removed.
 Mistake #10
Using two-way data binding for unsupported
properties
                                 Example
Flex 4 supports two-way data binding. You can create the binding by adding @ in front
of one of the curly braces, while leaving the other field unbound. For example, if you
run the application below and type a value in one text field the value is mirrored in the
other text field:
       Two-way databinding fails
Two-way data binding works in most cases; however, it does not work with
style or effect properties. It also does not work with the arguments property
for RemoteObject or the request property for HttpService, RemoteObject, or
WebService.
TurboBinding
             Q&A




@EladElrom

				
DOCUMENT INFO
Shared By:
Categories:
Tags:
Stats:
views:4
posted:11/10/2011
language:English
pages:60