Flex - how to detect if Object DATA has changed? - apache-flex

In Flex (Flash Builder 4), I need a way to know if something in an array collection has changed.
I have a custom object:
[Bindable]
public var _myobject:MyObject;
It's basically just a class containing a bunch of different String and Number properties.
I need a reliable way to know if any of the properties have been changed. For example, I am binding the properties to a user interface (fields), but it's also possible for some of the properties to change through code.
Is there a way to do this? I found ChangeWatcher, but that looks like it only looks at a single simple property, such as a String or Number. I need to watch or detect changes in all the properties in my object, hopefully without having to add ChangeWatcher events to every property. Is this possible?

You're probably better off just dispatching binding events on the specific properties you want bindable. Better yet, dispatch a custom binding event, so that all of the things that are bound don't have to filter for "is this the property I care about?" It's really easy with Flash Builder 4.5 to do this, just select your variable name and press Ctrl-1, select "Create getter and setter," select getter and setter and check "Bindable" and "create custom event."
This will create code for you that looks something like this:
private var _yourProperty:String;
[Bindable (event='yourPropertyChange')]
public function get yourProperty():String {
return _yourProperty;
}
public function set yourProperty(value:String):void {
if (value !=== _yourProperty) {
_yourProperty = value;
dispatchEvent(new Event('yourPropertyChange'));
}
}
This will be much less verbose and more performant than the code that Flash Builder generates for you behind the scenes when you just use the Bindable tag on its own.

If you use defined classes as VO/DAO and apply the [Bindable] tag to the class, this will do binding on all properties for you (so long as they are read/write).
Anonymous object detection is difficult at best, let alone adding additional headaches of loosing compiler type checking.
Super basic example: - the key is to tie it to the dispatcher, so internally it can send out the PropertyChangeEvent.
[Bindable]
public class Actor extends EventDispatcher
{
public var fName:String;
public var lName:String;
public var age:uint;
public function get displayName():String
{
return lName +', '+ fName;
}
public function Actor()
{
super();
}
}

public class BindableDictionary extends EventDispatcher {
public function BindableDictionary() {
super();
}
public var dictionary:Dictionary = new Dictionary();
[Bindable("change")]
public function get(key:Object):Object {
return dictionary[key];
}
public function put(key:Object, value:Object):void {
dictionary[key] = value;
dispatchEvent(new Event(Event.CHANGE));
}
}
maybe this class will give you some new idea

Related

How to make JavaFX ListProperty modifiable only through custom methods

I have a private list and I don't want that it can be modified from outside in general. Just adding from outside is allowed and only if the object is valid. Therefore I used to write it like this:
private List<Object> list = new ArrayList<>();
public List<Object> getList()
{
return Collections.unmodifiableList(list);
}
public void addObject(Object object)
{
if (isObjectValid(object)) //any validation
list.add(object);
}
Now for JavaFX purposes I turn the list to a property:
private ListProperty<Object> list =
new SimpleListProperty<>(FXCollections.observableArrayList());
To profit from the benefits of an property like data binding and the ListChangeListener I have to provide the property to the outer world. But then access to all methods of a list is provided, too. (To use a ReadOnlyListProperty has no effect since the list instance itself will never change.) What can I do to achieve all goals:
private ListProperty
ListChangeListener can be added from outside
Usable for binding dependencies from outside (the create methods form class Bindings)
No modifying of the property or list instance itself
Modifying of list content from outside only through my own methods
Not tested, but try:
private ListProperty<Object> list = new SimpleListProperty<>(FXCollections.observableArrayList());
private ReadOnlyListWrapper<Object> publicList = new ReadOnlyListWrapper<>();
and in the constructor:
publicList.bind(Bindings.createObjectBinding(
() -> FXCollections.unmodifiableObservableList(list.getValue()),
list));
then your accessor method is
public ReadOnlyListProperty<Object> getList() {
return publicList.getReadOnlyProperty();
}

Flex: Help me understand data Binding on getters and setters

Help me understand data Binding
When I create a variable in a class:
[Bindable] private var _name:String;
and then generate getters and setters, I get:
private var _name:String;
[Bindable]
public function get name():String
{
return _name;
}
public function set name(value:String):void
{
_name = value;
}
Why does it generate the tag '[Bindable]' only on the get function?
To me, it would seem that it should be on the set function, as I would want to know when the
value changes, not when the value is just read.
What might help to understand what is going on here is the code that the MXML compiler will generate for you when you make something [Bindable]. The MXML compiler wraps your [Bindable] property in it's own getter/setter. It does this so that the wrapper setter method can dispatch a "propertyChange" event when a new value is set. This event notifies the parties binding to the property that the value has changed.
Getters/setters in Actionscript are considered to be properties of the object (they are not methods of the object). So it doesn't matter whether your annotate the getter or the setter as [Bindable], the generated code does the right thing.
It's worth noting that you can avoid the generated code and optimize the situation by dispatching your own event when your property changes. To do this, your [Bindable] metadata tag needs to include the event name that will be dispatched when the property changes:
private var _name:String;
[Bindable("nameChanged")]
public function get name():String
{
return _name;
}
public function set name(value:String)
{
if (_name == value)
return;
_name = value;
dispatchEvent(new Event("nameChanged"));
}
Because the bindable metadata contains an event string, no extra code is generated. Note, the compiler won't warn you if you forget to dispatch the event from the setter. In fact, you can dispatch your custom binding event from anywhere in your class (this can be useful with functions that are bindable).

Flex event will only fire once

In an AIR application, I have a private variable and a setter:
private var _saveResult
public function set saveResult( result:String ):void
{
_saveResult = result;
dispatchEvent( new resultUpdatedEvent( _saveResult ));
}
The first time that I set "saveResult" the event fires. But it will never fire again unless I restart the application.
If I change the setter to:
public function set saveResult( result:String ):void
{
_saveResult = result;
if ( result != null)
{
dispatchEvent( new resultUpdatedEvent( _saveResult ));
}
}
The problem goes away, I can set the variable many times and the event fires every time.
My question:
Am I doing something wrong here? If not, can anyone explain to me whats happening? If so, what SHOULD I be doing?
Thanks!
It looks like you're constructing your event incorrectly. The first parameter of an Event object should always be a string. So in this case you'd want to always use the same string so you could listen for the event. What does your resultUpdatedEvent class look like? You'll want it to look something like this:
package myEvents
{
import flash.events.Event;
public class PropertyChangeEvent extends Event
{
public static const PROPERTY_CHANGE:String = "propertyChange";
public var result:String = "";
// Public constructor.
public function PropertyChangeEvent (type:String,
result:String="") {
// Call the constructor of the superclass.
super(type);
// Set the new property.
this.result= result;
}
// Override the inherited clone() method.
override public function clone():Event {
return new PropertyChangeEvent(type, result);
}
}
}
That way, when you go to dispatch your event, you can construct the event as follows:
new PropertyChangeEvent(PropertyChangeEvent.PROPERTY_CHANGE, result);
That way, you're listening for the event "PropertyChangeEvent.PROPERTY_CHANGE", which never changes. The problem is now your event listener is probably listening for an event represented by the string saved in result, and obviously, this changes after the first time it's set, so there's no way to assign a listener to it.
For more information about how events work in AS3: http://livedocs.adobe.com/flex/3/html/help.html?content=events_02.html
Per the comments...
There was no event dispatcher problem.
I misdiagnosed the problem, the REAL problem was that if you have a [Bindable] property and you use a setter, and you set it for the current value, flex will ignore it. SO, you have several choices:
1) give the getter and setter different names. Seems like a "bad idea" but it does fix the problem.
2) remove [Bindable] from either the class (my problem) or the property. If the class does not implement IEventDispatcher, you will need to do so. You can simply "extends Sprite" to see it work, but that seems like a "bad idea" as a solution, so I implemented IEventDispatcher per the example at the end of this page: http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/flash/events/IEventDispatcher.html
3) I am sure that there is a way to get around this bug, but I don't actually NEED the class to be [Bindable] so I did not find a work around.

Is it possible to remove properties from a dynamic class?

I have a dynamic ActionScript Class that is used to send parameters to a WebService. Some of these parameters are always present, so they are public properties of the Class:
package
{
[Bindable]
public dynamic class WebServiceCriteria
{
public var property1:int;
public var property2:String;
public var property3:String;
public var property4:String;
}
}
But, I am also adding properties at runtime that can change over time:
criteria.runTimeProperty = "1";
I'm not very familiar with using dynamic classes, so I was wondering if it is possible to "remove" the new property. Let's say the next time I call the WebService I don't want that property sent - not even as a null. How can I remove it from the Class instance without creating a new instance each time?
I believe all you'd need to do is this:
delete criteria.runTimeProperty;
or
delete criteria["runTimeProperty"];
Either should do the same thing.
See the delete documentation for specifics.

What is the best way to associate an Event with a Class in ActionScript / Flex 3?

ActionScript 3 / Flex 3 - Adding custom events to a class
Say I have the following Event:
import flash.events.Event;
public class SomeEvent extends Event
{
public static const EVENT_ACTION:String = "eventAction";
public function SomeEvent(type:String) {
super(type);
}
override public function clone():Event {
return new SomeEvent(this.type);
}
}
... and the following Class:
public class SomeClass extends EventDispatcher
{
public function someFunction():void
{
dispatchEvent(new SomeEvent("eventAction"));
}
}
What is the best way to show that 'SomeClass' throws 'SomeEvent'? The only way I have found is to decorate 'SomeClass' with the [Event] attribute, as follows:
[Event (name="eventAction", type="SomeEvent")]
This allows me to instantiate the class and add an event listener by doing this:
var someClassInstance:SomeClass = new SomeClass();
someClassInstance.addEventListener(SomeEvent.EVENT_ACTION, mycallbackmethod);
Is there a better way to do this? Putting the [Event] attribute on the class followed by some string literals just feels ... wrong. Thanks in advance for the help!
You're doing it right. Currently, the AS3 compiler allows only string literals in metadata. Constants cannot be used.
By the way, Adobe's public bug database has a feature request to allow ActionScript constants in metadata. Feel free to vote for it.
I know, I feel the same way; using the string literals does seem a bit wrong somehow. But to the best of my knowledge, from all the documentation I've seen and the talks I've attended and in reading the Flex source code, etc., it does appear that's the proper way to do it.

Resources