60
Flex data binding pitfalls: 10 common misuses and mistakes @EladElrom

Flex data binding pitfalls: 10 common misuses and mistakes

Embed Size (px)

DESCRIPTION

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‎. - PowerPoint PPT Presentation

Citation preview

Page 1: Flex data binding pitfalls:  10 common misuses and mistakes

Flex data binding pitfalls: 10 common misuses and mistakes

@EladElrom

Page 2: Flex data binding pitfalls:  10 common misuses and mistakes

@EladElrom• Associate Dev Director @ Sigma Group• Senior Flash Engineer & Lead• Technical Writer• FlashAndTheCity Organizer• Adobe Community Professional

Page 3: Flex data binding pitfalls:  10 common misuses and mistakes

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.

Page 4: Flex data binding pitfalls:  10 common misuses and mistakes

TOCFlex data binding pitfalls: common misuses and mistakes

1. Missing silent errors2. Trying to bind a class that does not include the

IPropertyChangeNotifier interface3. Using binding in place of direct assignment4. Forgetting to unbind and risking memory leaks5. Not changing the default propertyChange event constant6. Using the wrong bindable event name7. Assuming an execution order for binding8. Using binding in place of events9. Binding a class and its properties10. Using two-way data binding for unsupported properties

Lastly, Turbo Binding

Page 5: Flex data binding pitfalls:  10 common misuses and mistakes

Mistake #1: Missing silent errors

Page 6: Flex data binding pitfalls:  10 common misuses and mistakes

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.

Missing Silent Errors

Page 7: Flex data binding pitfalls:  10 common misuses and mistakes

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.

Why Are Errors being Silently Captured?

Page 8: Flex data binding pitfalls:  10 common misuses and mistakes

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.

Binding Error Example

Page 9: Flex data binding pitfalls:  10 common misuses and mistakes

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.

Debugging Binding

Page 10: Flex data binding pitfalls:  10 common misuses and mistakes

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.

Binding and BindingManager

Page 11: Flex data binding pitfalls:  10 common misuses and mistakes

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.

Debugging Binding

Page 12: Flex data binding pitfalls:  10 common misuses and mistakes

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.

Null Errors Examples

Page 13: Flex data binding pitfalls:  10 common misuses and mistakes

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

Page 14: Flex data binding pitfalls:  10 common misuses and mistakes

Debugging Binding Console

Page 15: Flex data binding pitfalls:  10 common misuses and mistakes

Trying to bind a class that does not include the IPropertyChangeNotifier interface

Mistake #2

Page 16: Flex data binding pitfalls:  10 common misuses and mistakes

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.

IPropertyChangeNotifier Marker

Page 17: Flex data binding pitfalls:  10 common misuses and mistakes

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!

ValueObject

Page 18: Flex data binding pitfalls:  10 common misuses and mistakes

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):

Reason

Page 19: Flex data binding pitfalls:  10 common misuses and mistakes

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.

Solution

Page 20: Flex data binding pitfalls:  10 common misuses and mistakes

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.

ObjectProxy Class

Page 21: Flex data binding pitfalls:  10 common misuses and mistakes

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.

Page 22: Flex data binding pitfalls:  10 common misuses and mistakes

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.

ObjectProxy caveat

Page 23: Flex data binding pitfalls:  10 common misuses and mistakes

Mistake #3: Using binding in place of direct assignment

Page 24: Flex data binding pitfalls:  10 common misuses and mistakes

What’s wrong with the code below?

Page 25: Flex data binding pitfalls:  10 common misuses and mistakes

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.

Answer

Page 26: Flex data binding pitfalls:  10 common misuses and mistakes

“-keep” generated mxmlc codeclass BindableProperty{

/* * generated bindable wrapper for property someTextInput (public) * - generated setter * - generated getter * - original public var 'someTextInput' moved to '_2040386569someTextInput' */

[Bindable(event="propertyChange")] public function get someTextInput():spark.components.TextInput { return this._2040386569someTextInput; }

public function set someTextInput(value:spark.components.TextInput):void { var oldValue:Object = this._2040386569someTextInput; if (oldValue !== value) { this._2040386569someTextInput = value; if (this.hasEventListener("propertyChange")) this.dispatchEvent(mx.events.PropertyChangeEvent.createUpdateEvent(this, "someTextInput", oldValue, value)); } }

/* * generated bindable wrapper for property someText (private) * - generated setter * - generated getter * - original private var 'someText' moved to '_1504842817someText' */

[Bindable(event="propertyChange")] private function get someText():String { return this._1504842817someText; }

private function set someText(value:String):void { var oldValue:Object = this._1504842817someText; if (oldValue !== value) { this._1504842817someText = value; if (this.hasEventListener("propertyChange")) this.dispatchEvent(mx.events.PropertyChangeEvent.createUpdateEvent(this, "someText", oldValue, value)); } }

}

package{[ExcludeClass]public class _TesterWatcherSetupUtil implements mx.binding.IWatcherSetupUtil2{ public function _TesterWatcherSetupUtil() { super(); }

public static function init(fbs:IFlexModuleFactory):void { import Tester; (Tester).watcherSetupUtil = new _TesterWatcherSetupUtil(); }

public function setup(target:Object, propertyGetter:Function, staticPropertyGetter:Function, bindings:Array, watchers:Array):void {

// writeWatcher id=0 shouldWriteSelf=true class=flex2.compiler.as3.binding.PropertyWatcher shouldWriteChildren=true watchers[0] = new mx.binding.PropertyWatcher("someText", { propertyChange: true }, // writeWatcherListeners id=0 size=1 [ bindings[0] ], propertyGetter);

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

Page 27: Flex data binding pitfalls:  10 common misuses and mistakes

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.

Rule of thumb

Page 28: Flex data binding pitfalls:  10 common misuses and mistakes

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.

Direct Assignment

Page 29: Flex data binding pitfalls:  10 common misuses and mistakes

Forgetting to unbind and risking memory leaks

Mistake #4

Page 30: Flex data binding pitfalls:  10 common misuses and mistakes

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.

BindingUtils

Page 31: Flex data binding pitfalls:  10 common misuses and mistakes

site = destinationhost = 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.

public static function bindProperty( site:Object, prop:String, host:Object, chain:Object, commitOnly:Boolean = false, useWeakReference:Boolean = false):ChangeWatcher

bindProperty

Page 32: Flex data binding pitfalls:  10 common misuses and mistakes

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.

bindProperty Example

Page 33: Flex data binding pitfalls:  10 common misuses and mistakes

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

Page 34: Flex data binding pitfalls:  10 common misuses and mistakes

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!

bindSetter Example

Page 35: Flex data binding pitfalls:  10 common misuses and mistakes

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

Avoid Memory leak

Page 36: Flex data binding pitfalls:  10 common misuses and mistakes

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.

Example

Page 37: Flex data binding pitfalls:  10 common misuses and mistakes

Not changing the default propertyChange event constant

Mistake #5

Page 38: Flex data binding pitfalls:  10 common misuses and mistakes

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.

Default type

Page 39: Flex data binding pitfalls:  10 common misuses and mistakes

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.

Compiler creates a lot of code

Page 40: Flex data binding pitfalls:  10 common misuses and mistakes

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.

Do it yourself

Page 41: Flex data binding pitfalls:  10 common misuses and mistakes

Using the wrong bindable event name

Mistake #6

Page 42: Flex data binding pitfalls:  10 common misuses and mistakes

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:

What’s wrong with this code?

Page 43: Flex data binding pitfalls:  10 common misuses and mistakes

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:

Answer

Page 44: Flex data binding pitfalls:  10 common misuses and mistakes

Assuming an execution order for binding* This one is a very common one!

Mistake #7

Page 45: Flex data binding pitfalls:  10 common misuses and mistakes

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.

Common Mistake

Page 46: Flex data binding pitfalls:  10 common misuses and mistakes

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.

Example

Page 47: Flex data binding pitfalls:  10 common misuses and mistakes

Compile time error

Page 48: Flex data binding pitfalls:  10 common misuses and mistakes

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.

Another Example

Page 49: Flex data binding pitfalls:  10 common misuses and mistakes

Using binding in place of events

Mistake #8

Page 50: Flex data binding pitfalls:  10 common misuses and mistakes

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.

Explanation

Page 51: Flex data binding pitfalls:  10 common misuses and mistakes

Example

It looks like pretty standard code for a Flex application. However, the type of binding illustrated here is not needed.

Page 52: Flex data binding pitfalls:  10 common misuses and mistakes

use direct assignment in an event handler:

Better way…

Page 53: Flex data binding pitfalls:  10 common misuses and mistakes

Binding a class and its properties

Mistake #9

Page 54: Flex data binding pitfalls:  10 common misuses and mistakes

Another common mistake is to set a class to be bindable and then make each property in the class bindable as well; for example:

Example

Page 55: Flex data binding pitfalls:  10 common misuses and mistakes

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.

Compile time warnings

Page 56: Flex data binding pitfalls:  10 common misuses and mistakes

Using two-way data binding for unsupported properties

Mistake #10

Page 57: Flex data binding pitfalls:  10 common misuses and mistakes

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:

Example

Page 58: Flex data binding pitfalls:  10 common misuses and mistakes

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.

Two-way databinding fails

Page 59: Flex data binding pitfalls:  10 common misuses and mistakes

TurboBinding

Page 60: Flex data binding pitfalls:  10 common misuses and mistakes

Q&A

@EladElrom