Flex watch bindable property other class - apache-flex

I'm creating an application using Flex 4.
When the app is started, it reads a XML file and populate objects. The .send() call is asynchronous, so I would want to listen/watch to this populated object, and when it has finished, dispatch an event for other classes, so they can use it.
package model{
public class LectureService extends HTTPService{
[Bindable]
private var _lecture:Lecture;
...
}
The xml is parsed correctly and loaded inside the object lecture of the class Lecture.
If I use the MXML notation in the main.mxml app, it works fine (the object is used when the it is populated after the async request):
<mx:Image id="currentSlide" source={lectureService.lecture.slides.getItemAt(0).path} />
BUT, I have another ActionScript class and I'm not able to listen to this dispatched (by [Bindable]) event.
package components{
public class LectureSlideDisplay extends Image
{
public function LectureSlideDisplay()
{
super();
this.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, onChangeTest);
}
private function onChangeTest(e:PropertyChangeEvent):void {
trace('test');
}
I have already tried:
using (like above) addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, methodName).
tried to change the [Bindable] to [Bindalbe("nameEvent")] and listen for this, nothing.
using CreateWatcher method, doesn't work.
tried to have a look to the generated code of the class, but didn't help me
if (this.hasEventListener("propertyChange")){
this.dispatchEvent(mx.events.PropertyChangeEvent.createUpdateEvent(this, "lecture", oldValue, value));
}
How can I listen and have the populated object in another class?
Maybe the problem is that I'm listening from another class, but in this case how can I implement this?
It seems the event is dispatched, but I can't listen to it.

For who wants the answer, I have resolved changing the addEventListener object.
It is not right to use:
this.addEventListener(...)
Use instead:
lectureService.addEventListener(...)
I have changed my code to listen to this event in the main app MXML, and then inside the handler method, call the public method of your components to use the new data.

You can't solve all your problems by just extending the class. You should really look into Commands for your HTTP requests.
The change property event is used internally for watchers and won't dispatch in the overall component. What you want to do for your LectureSlideDisplay is to override the source setter. Every time it is called, a new value is being binded to it:
package components{
public class LectureSlideDisplay extends Image
{
override public function set source(value:Object):void
{
super.source = value;
// do whatever
}
public function LectureSlideDisplay()
{
super();
}
}
}
You should really read up on how Binding works.

Consider to use BindingUtils class. You can found documentation here. And a couple of usage samples: one, two.

Related

Actionscript3 / LCCS: how to access property parent class protected var?

I would like to assign an event listener to a protected timer variable in the parent class.
I am working with Adobe LCCS, and created a BatonProperty
which implements Baton
Now, Baton has a protected timer variable declared like this, and for some reason, I am unable to get access to this _autoPutDownTimer, from a BatonProperty instance.
public function Baton()
{
super();
_autoPutDownTimer = new Timer(_timeout*1000, 1);
_autoPutDownTimer.addEventListener(TimerEvent.TIMER_COMPLETE, onTimerComplete);
invalidator.addEventListener(Invalidator.INVALIDATION_COMPLETE,commitP roperties);
}
I would love to attach an eventlistener to it like this, but it seems I cannot, any tips are truly appreciated:
public var mybatonprop:BatonProperty;
mybatonprop= new BatonProperty();
mybatonprop.sharedID = "myBaton";
mybatonprop.subscribe();
mybatonprop.baton._autoPutDownTimer.addEventListener(TimerEvent.TIMER,countDown);
This gives the error : "actionscript attempted access of inaccessible property through a reference with static type" But it seems that property is not private, only protected, since it is declared like this
/**
* #private
*/
protected var _autoPutDownTimer:Timer;
If BatonProperty extends Baton, then just change the line:
mybatonprop.baton._autoPutDownTimer.addEventListener(TimerEvent.TIMER,countDown);
to
mybatonprop._autoPutDownTimer.addEventListener(TimerEvent.TIMER,countDown);
otherwise, make _autoPutDownTimer public, or follow #Jacob's answer.
Or for a third suggestion, as Baton is an Adobe class and you can't edit it, create your own class MyBaton which extends Baton, then do either of the two suggestions. (MyBaton will work everywhere Baton does)
protected means private except to descendants of the class. i.e. classes that inherit from the parent. It looks like your BatonProperty uses Baton via composition, not inheritance. And, from what I can tell, it seems like you're trying to access the _autoPutDownTimer from Baton/BatonProperty via a third class.
My recommendation, though would not be to add the event listener directly to the timer, but to dispatch an event from Baton in the onTimerComplete function
protected function onTimerComplete(event:TimerEvent):void {
....
dispatchEvent(new Event('putDownComplete'));
}
and
mybatonprop.baton.addEventListener('putDownComplete', onPutdownComplete);

Flex 3 event bubbling set to false.. how to make it bubble then?

I have a flex app with lots of nested views and popup windows..
I'd love to catch all the CHANGE events in the application at the top level.. all of them, simply to notify the user that he has changed something (trust me it makes sense in my app).
Now, I tried to add an event listener in the Application creationComplete handler like this:
private function init():void {
this.addEventListener(flash.events.Event.CHANGE, function f():void {...})
}
but it does not work.. why? I read in the docs that event bubbling for the CHANGE event is set to false before dispatching. How can I change that? Is there any other way to achieve my goal?
thanks
Try listening to events on the SystemManager instead of the Application. As far as I understand, SystemManager sits at the very top of the display list, adding the application, popups and other UI entities as children.
In Flex 3 and below, you can retrieve it via Application.application.systemManager.
Read more on the SystemManager on Deepa's blog:
http://iamdeepa.com/blog/?p=11
I am also having trouble with a group of TextArea controls where I would like to listen for the change event on their container (parent) instead.
What I did in the end was to extend the TextArea class and create a listener for the change event. From the listener I would then dispatch a custom event that could bubble.
public class BubblingTextArea extends TextArea
{
public function BubblingTextArea()
{
super();
addEventListener(TextOperationEvent.CHANGE, changeHandler);
}
private function changeHandler(event:TextOperationEvent):void
{
dispatchEvent(new ChangeBubbleEvent(ChangeBubbleEvent.BUBBLE_CHANGE));
}
}
The custom event:
public class ChangeBubbleEvent extends Event
{
public static const BUBBLE_CHANGE:String = "bubbleChange";
public function ChangeBubbleEvent(type:String, bubbles:Boolean=true, cancelable:Boolean=false)
{
super(type, bubbles, cancelable);
}
}
I am sure someone can come up with a more elegant solution since I am still quite new to Flex and AS3 myself.
As far as I know, PopUps happen outside of the Application's main displayList, so that's probably why you're not seeing bubbling. In this case, you'll need to manually add listeners to popups. The Flash change event does bubble according to the docs: http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/events/Event.html#CHANGE
I personally like to use a framework such as RobotLegs or Parsley.
The basic idea is that each view and popup gets a mediator. The mediator's job is to communicate between the view and the command/model. Those mediators can listen directly to the view and the view's components.

How do I make sure the text of an ActionScript TextInput is updated when the Object property defining that text is updated?

Not an easy question to decipher, so let me boil it down. I'm trying to convert an MXML component to an ActionScript Class. The component consists of a Form with a TextInput, TextArea, and two buttons - Save and Cancel, and a Validator for the TextInput, and other logic to handle events that occur. This component is currently extended by several other components.
Now, in the MXML component binding the TextInput text property to a property in an Object was very easy:
<mx:TextInput text="{_itemToEdit.name}" />
But in ActionScript, I'm creating the TextInput and setting the text property before the Object is set, and the TextInput is not being updated:
public var itemToEdit:Object = {};
private var nameInput:TextInput = new TextInput();
public function MyClass()
{
nameInput.text = itemToEdit.name;
}
How can I make sure that the TextInput text property is bound to the specified property in the Object?
Binding is all about firing change events. you'll need to modify your 'itemToEdit' class to be an EventDispatcher for this hack to work. here goes
//item to edit class
private var _name:String;
public function set name(value:String):void
{
_name = value;
dispatchEvent(new Event("NAME_CHANGED"));
}
//whatever the binding class is
private var _itemToEdit:ItemToEdit;
private var _textField:TextField;
public function set itemToEdit(value:ItemToEdit):void
{
if (_itemToEdit) removeEventListeners();
_itemToEdit = value;
if (_itemToEdit) addEventListeners();
}
private function addEventListeners():void
{
_itemToEdit.addEventListener("NAME_CHANGED", itemToEdit_nameChangedHandler);
itemToEditChangedHandler(null);
}
private function itemToEdit_nameChangedHandler(event:Event):void
{
_textField.text = _itemToEdit.name;
}
Obviously this was done just for speed, you'll need custom events and some better names etc, but this is the basic jist.
Apparently it's slightly more complex than a simple assignment to bind purely in AS, here's a couple tutorial/docs to show you how to pull it off.
http://cookbooks.adobe.com/index.cfm?event=showdetails&postId=6802
http://raghuonflex.wordpress.com/2007/08/30/binding-in-mxml-as/
Compile your MXML component with the -keep option. Examine the ActionScript code that was generated by mxmlc and do something similar.
You may also do it using the Proxy object - I blogged about it over here: http://flexblog.faratasystems.com/?p=433
If "itemToEdit" is a pure AS3 Object, then the binding probably doesn't work properly anyway. That is, it will work when the object is initially created, but any changes to "name" in the object won't be detected. (I could be wrong...haven't done extensive tests)
Anyway, your problem is easy to solve with getters/setters:
private var _itemToEdit:Object;
public function get itemToEdit():Object { return _itemToEdit; }
public function set itemToEdit(value:Objecy):void {
_itemToEdit = value;
nameInput.text = value.name;
}
Binding isn't necessary here.

Why is 'textField' not instantiated when I subclass TextArea in Flex?

I'm experimenting with TextArea and inheritance to implement some additional functionality on the protected textField property.
Unfortunately, when I create a new instance of the subclass, this property is set to null. I'm probably misunderstanding the way super() works, but I thought it would have been instantiated after the constructor finished.
Here's a small snippet of code which extends TextArea:
public final class ExtTextArea extends TextArea {
public function ExtTextArea() {
super();
}
public function testTextField():void {
if (textField == null)
Alert.show("null!");
}
}
}
The invoking code is simple:
var extTextArea:ExtTextArea = new ExtTextArea();
extTextArea.testTextField();
The Alert in ExtTestArea appears every time I run this code.
Why is this? Is there something more I need to do to access the textField property?
Since textField is "the internal UITextField that renders the text of this TextArea" I believe it will remain null until you add it to the display via .addChild(...). I ran a quick test to verify that once I've added it to the display, it is no longer null. You might want to add an event handler to the "creation complete" event and adjust it at that point (I think).
The Flex SDK comes with source code, so you can take a peek and see when this field is initialized. It is not initialized in the constrcutor, but you will see that a new TextField instantiated by createChildren(), which is called when the component is added to a layout container.

How to raise an event from a SWF in a SWFLoader to a parent Flex application?

How can I raise an event from a SWF file loaded into a Flex application (using SWFLoader)?
I want to be able to detect
a) when a button is pressed
b) when the animation ends
You'll need to do 2 things:
Dispatch an event from the loaded swf. Make sure the event bubbles if you sent it from nested views. Bubbling can be set through the bubbles property of the event.
Listen to the event from your main application. I think you should be able to do that on the content property of the SWFLoader instance.
mySWFLoader.content.addEventListener("myEvent", myEventHandler);
I took a lazier approach for raising the event inside flash
Flex:
<mx:SWFLoader source="homeanimations/tired.swf" id="swfTired" complete="swfTiredLoaded(event)" />
private function swfTiredLoaded(event:Event): void {
mySWFLoader.content.addEventListener("continueClicked", continueClickedHandler);
}
Flash:
dispatchEvent(new Event("continueClicked", true, true));
I believe its because you would be creating two seperate custom event class one in Flash and the other in Flex.
Dispatching one EV_NOTIFY.ANIMATION_ENDED from Flash may not be understood by Flex,since it has its own version of EV_NOTIFY.ANIMATION_ENDED.
As an adjunct to the answer by Christophe Herreman, and in case you were wondering, here is a way of making your own events...
package yourpackage.events
{
import flash.events.Event;
[Event(name="EV_Notify", type="yourpackage.events.EV_Notify")]
public class EV_Notify extends Event
{
public function EV_Notify(bubbles:Boolean=true, cancelable:Boolean=false)
{
super("EV_Notify", bubbles, cancelable);
}
}
}
I have taken the liberty of setting the default value of bubbles to true and passing the custom event type to the super constructor by default, so you can then just say...
dispatchEvent(new EV_Notify());
In your particular case I doubt there are times when you would not want your event to bubble.
The prefix EV_ on the name is my own convention for events so I can easily find them in the code completion popups, you'll obviously pick your own name.
For the two cases you cite you can either have two events and listen for both of them, or add a property to the event which says what just happened, which is the approach which is taken by controls like Alert...
package yourpackage.events
{
import flash.events.Event;
[Event(name="EV_Notify", type="yourpackage.events.EV_Notify")]
public class EV_Notify extends Event
{
public static var BUTTON_PRESSED:int = 1;
public static var ANIMATION_ENDED:int = 2;
public var whatHappened:int;
public function EV_Notify(whatHappened:int, bubbles:Boolean=true, cancelable:Boolean=false)
{
this.whatHappened = whatHappened;
super("EV_Notify", bubbles, cancelable);
}
}
}
then you call it as follows...
dispatchEvent(new EV_Notify(EV_NOTIFY.ANIMATION_ENDED));
you can then inspect the whatHappened field in your event handler.
private function handleNotify(ev:EV_Notify):void
{
if (ev.whatHappened == EV_Notify.ANIMATION_ENDED)
{
// do something
}
else if (ev.whatHappened == EV_Notify.BUTTON_PRESSED)
{
// do something else
}
etc...
}
HTH
I could not make this last approach work (with Flash CS4 and Flex 3). I put the dispatchEvent call in one of the last frames of my Flash animation, but could not pick it up in Flex.
I resorted to a counter variable and incrementing until I reached the known last frame number using the ENTER_FRAME event - which I can pick up using almost the same code.
If I can pick this up, then why can't I pick up a custom event?

Resources