What is the difference between target and currenttarget in flex? - actionscript-3

Can any one please tell me the difference between target and currenttarget in flex?

Sure, I've had some trouble with this too. The currentTarget property is the IEventListener you registered the event handler for. The target is the one that dispatched the event that you are currently handling. So the currentTarget changes, the target doesn't.
Check out the following example:
Sample App
<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
creationComplete="addListeners()">
<mx:Script>
<![CDATA[
protected function addListeners():void
{
greatGrandParent.addEventListener(Event.COMPLETE, completeHandler);
grandParent.addEventListener(Event.COMPLETE, completeHandler);
aParent.addEventListener(Event.COMPLETE, completeHandler);
child.addEventListener(Event.COMPLETE, completeHandler);
// dispatch event that "bubbles", second param is "true"
// dispatched from child
child.dispatchEvent(new Event(Event.COMPLETE, true));
}
protected function completeHandler(event:Event):void
{
trace("target: ", event.target + ", currentTarget: ", event.currentTarget);
}
]]>
</mx:Script>
<mx:Panel id="greatGrandParent">
<mx:Panel id="grandParent">
<mx:Panel id="aParent">
<mx:Button id="child"/>
</mx:Panel>
</mx:Panel>
</mx:Panel>
</mx:Application>
Output
target: MyApp.greatGrandParent.grandParent.aParent.child, currentTarget: MyApp.greatGrandParent.grandParent.aParent.child
target: MyApp.greatGrandParent.grandParent.aParent.child, currentTarget: MyApp.greatGrandParent.grandParent.aParent
target: MyApp.greatGrandParent.grandParent.aParent.child, currentTarget: MyApp.greatGrandParent.grandParent
target: MyApp.greatGrandParent.grandParent.aParent.child, currentTarget: MyApp.greatGrandParent
It's a simple tree of display objects, and when the app is ready I:
Add listeners for the same event on each component in the tree.
Dispatch an arbitrary event (just for demonstration). I chose Event.COMPLETE.
Since everything has registered an eventHandler for that same event, and since I have set bubbles to true (new Event(type, bubbles)), anything in the tree, from child to greatGrandParent and beyond, that has registered an event handler for Event.COMPLETE, will run that method: completeHandler. Events travel up the chain then back down. The target is the one that dispatched the event, so since child dispatched it, it should be constant. The currentTarget is what changes.
This means that, say you want to check when you are rolling over a DataGrid in Flex, you want to know when you roll over a Checkbox inside one of the itemRenderers in the DataGrid. One way is to addEventListener on every itemRenderer's checkbox for MouseEvent.ROLL_OVER. Another way is to addEventListener to the DataGrid itself for MouseEvent.ROLL_OVER, and check what the target is on the event:
protected function dataGrid_rollOverHandler(event:MouseEvent):void
{
// event.currentTarget is DataGrid
if (event.target is CheckBox)
trace("rolled over checkbox!");
}
That's how I often use event.target.
Hope that helps,
Lance

Thus could help :
http://livedocs.adobe.com/flex/3/html/help.html?content=events_08.html#219548

You should go through tutorials on this site: http://www.adobe.com/devnet/flex/videotraining/ for an introduction to Flex before asking a question like this. Your question is covered on day 1.

Related

Flex: preventDefault on spark:ListBase not working

The proposed UX I am trying to achieve is this:
user clicks menu item (via a listBase subclass: e.g. ButtonBar or TabBar)
prevent initial selection
validate if user needs to address issues (e.g. unsaved data on a form, etc.)
if valid, take selection and set the listBase to that selectedIndex, otherwise present warnings to user and cancel out the selection process altogether
This does not work as you'd expect. Utilizing the IndexChangeEvent.CHANGING type and the preventDefault works to kill the selection, but at step 4, when I am programmatically setting the selectedIndex of the listBase, it then tries to redispatch the CHANGING event (this despite what the API docs claim).
Here is a sample application src code if you'd like to try this for yourself. I look forward to your comments & solutions.
Thanks.
J
http://snipt.org/vUji3#expand
<?xml version="1.0" encoding="utf-8"?>
<s:Application minWidth="955" minHeight="600"
xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns:s="library://ns.adobe.com/flex/spark">
<fx:Script>
<![CDATA[
import flash.utils.setTimeout;
import mx.core.mx_internal;
import spark.events.IndexChangeEvent;
use namespace mx_internal;
[Bindable]
private var doPreventDefault:Boolean;
[Bindable]
private var delayMS:uint = 500;
private function buttonbar_changingHandler( event:IndexChangeEvent ):void
{
// TODO Auto-generated method stub
if ( doPreventDefault )
{
event.preventDefault();
setTimeout( delayedLogic, delayMS, event.newIndex );
}
}
private function delayedLogic( index:int ):void
{
//disabling this results in an endless loop of trying to set the selected index
// doPreventDefault = false;
//this should NOT be hitting the changing handler since we're supposed to be dispatching a value commit event instead.
bb.setSelectedIndex( index, false );
}
]]>
</fx:Script>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<s:layout>
<s:VerticalLayout horizontalAlign="center"/>
</s:layout>
<s:ButtonBar id="bb"
changing="buttonbar_changingHandler(event)">
<s:dataProvider>
<s:ArrayList>
<fx:String>btn 0</fx:String>
<fx:String>btn 1</fx:String>
<fx:String>btn 2</fx:String>
</s:ArrayList>
</s:dataProvider>
</s:ButtonBar>
<s:CheckBox label="preventDefault?"
selected="#{ doPreventDefault }"/>
<s:NumericStepper maximum="5000" minimum="500"
stepSize="500" value="#{ delayMS }"/>
</s:Application>
Looking at the SDK, the IndexChangeEvent.CHANGING event is actually preventable - despite the documentation here says that cancelable is false, so my bad on that (although ASDoc went a little sideways), however things get a little interesting from here.
In ListBase #1296 this is only ever dispatched from the commitSelection(dispatchEvents:Boolean = true) method. In ButtonBarBase:dataProvider_changeHandler() is the only place that specifically calls to not dispatch the event, but in ListBase, it's called in commitProperties #939 when there is a proposedSelectionIndex.
So from your code above, if you are trying to set the selection - this is going to call the commitSelection, which I believe is causing the call stack issue. The Timer delay is just going to exacerbate the issue, since at 500ms the UI will have gone through its invalidation cycle at least once, meaning the commitSelection will be executed again because of an invalidateProperties flag is being set from the proprosedSelectionIndex eventually stemming from setSelectedIndex #729
So how to fix this.
I would look at only doing the prevent if the validation fails, otherwise allow it to proceed as normal. If it does fail, call the prevent, set an errorString or equivalent, but don't attempt to change the selection.
[edit] Read RiaStar's comment, and I just concurred with the same 'solution'.

itemclick event in spark tabbar?

Hallo tabbar supports item click event. However spark tabBar doesnot supports itemClick event.
is there way to listen itemclick event in SPARK TABBAR
thanks all
Spark components inheriting from ListBase no longer dispatch ItemClick events. You can use the IndexChangeEvent event instead though. It has a property newIndex that tells you which is the newly selected item (or tab in this particular case).
<s:TabBar dataProvider="{dp}" change="trace('selected: ' + event.newIndex)" />
One big difference with the old ItemClick is that this event is only dispatched when the selected item actually changes (as opposed to whenever it is clicked). If you really want the behaviour of ItemClick back, you can create a custom ItemRenderer that dispatches an
ItemClick event.
If you want to react on every click there are a few approaches. Here are two of them:
1./ Create a custom ItemRenderer that dispatches an ItemClick event.
.
public class TabBarButton extends ButtonBarButton {
override public function initialize():void {
super.initialize();
addEventListener(MouseEvent.CLICK, fireItemClick);
}
private function fireItemClick(event:MouseEvent):void {
owner.dispatchEvent(new ItemClickEvent(
ItemClickEvent.ITEM_CLICK, false, false, null, itemIndex, null, data
))
}
}
You can now use it like this:
<s:TabBar id="tabBar" dataProvider="{dp}"
itemRenderer="net.riastar.TabBarButton" />
tabBar.addEventListener(ItemClickEvent.ITEM_CLICK, onItemClick);
2./ Another approach would be to just listen for any click event on the TabBar and use event.target to find the clicked tab:
<s:TabBar dataProvider="{dp}" click="trace(event.target)" />
//traces tabBar.TabBarSkin1.dataGroup.TabBarButton1
Note that this is a direct answer to your question, but I actually don't think you should do this. In most cases IndexChangeEvent.CHANGE will do just fine.

Flex popup window

I am creating modal popup canvas window in a parent page. When I close the popup how do we get notification in parent screen that child popup is just closed. Any event for that ?
The code to show your popup:
var popup:MyPopup = new popup:MyPopup();
popup.addEventListener(CloseEvent.CLOSE, function(evt) {
PopUpManager.removePopUp(popup);
});
PopUpManager.addPopUp(popup, this, true);
Inside your MyPopup class, you will have a button for closing the popup. Simply hook the click event to publish a "CLOSE" event:
<s:Button Label="X" click="dispatchEvent(new CloseEvent(CloseEvent.CLOSE));" />
I prefer this mechanism over having the MyPopup object calling PopUpManger.removePopUp (as #Fank is suggesting) because it couples the MyPopup component to the PopUpManager which I don't like. I'd prefer the user of MyPopup to decide how to use the component.
Honestly, though, these are two very similar mechanisms to perform the same end goal.
Yes there is:
I Preffer to use the Popupmanager:
Your Popup:
There is a Button "close" call a internal function eg.closeme
private function closeMe () :void
{
PopUpManager.removePopUp(this);
}
in your Parent, you open the PopUp like this:
private function openPopup () :void
{
var helpWindow:TitleWindow = TitleWindow(PopUpManager.createPopUp(this,MyTitleWindow,fale));
helpWindow.addEventListener(CloseEvent.CLOSE, onClose);
}
protected function onClose (event:CloseEvent) :void
{
PopUpManager.removePopUp (TitleWindow(event.currentTarget));
}
My TitleWindow is the name of your class of cour popup extended by TitleWindow.
BR
Frank
Along with Brian's answer don't forget to detach the event listener. If you leave the event handler in your main app listening for an event from a child object, the child object will not be garbage collected as something is still referencing it. This is a common memory leak issue.
popup.addEventListener(CloseEvent.CLOSE, popup_CloseHandler);
private function popup_CloseHandler(event:CloseEvent):void{
event.target.removeEventListener(CloseEvent.CLOSE, popup_CloseHandler);
PopUpManager.removePopUp(popup);
}
Here's a great post about Flex's memory management if you want to delve into that further.
http://blogagic.com/163/flex-memory-management-and-memory-leaks

Click to hit underneath

I have a UIComponent that sits on top of a grid visually, which is also a UIComponent. The grid and the UIComponent that sits on top are on different branches. The gird has children which are Sprites. When the MousEvent.CLICK is dispatched it goes to the UIComponent on top. What I want it to do is go the element of the grid underneath. Is this possible?
Yes you can,
if your element id is "ui_component"
then in ActionScript you should add two properties
ui_component.mouseEnabled = true;
ui_component.mouseChildren = true;
This will tell your component to pass click event on MovieClip behind it.
I hope this helps.
What I had to do was just make the UIComponent on top top.mousEnabled = false
Another option, if your implementation does not allow for you to disable the mouse events for the top UIComponent, is to manually redispatch the event.
Here's a rather generic example. Attach the following handler for a MouseEvent.CLICK to either the container that holds the other UIComponents or the one on the UIComponent on top:
private function propagateClickEvent(event:MouseEvent):void
{
// find all objects under the click location
var uicOver:Array = getObjectsUnderPoint(new Point(event.stageX, event.stageY));
// filter out what you don't want; in this case, I'm keeping only UIComponents
uicOver = uicOver.filter(isUIComponent);
// resolve skins to parent components
uicOver = uicOver.map(resolveSkinsToParents);
// remove the current item (otherwise this function will exec twice)
if (event.currentTarget != event.target) { // otherwise let the following line do the removal
uicOver.splice(uicOver.indexOf(event.currentTarget),1);
}
// remove the dispatching item (don't want to click it twice)
uicOver.splice(uicOver.indexOf(event.target),1);
// dispatch a click for everything else
for each (var uic:UIComponent in uicOver) {
uic.dispatchEvent(new MouseEvent(MouseEvent.CLICK, false)); // no bubbling!
}
}
// test if array item is a UIComponent
private function isUIComponent(item:*, index:int, arr:Array):Boolean {
return item is UIComponent;
}
// resolve Skins to parent components
private function resolveSkinsToParents(item:*, index:int, arr:Array):* {
if (item is Skin) {
return item.parent;
}
return item;
}
This code would work as follows in MXML:
<s:Group click="propagateClickEvent(event)">
<s:Button click="trace('button1')"/>
<s:Button click="trace('button2')"/>
<s:Button click="trace('button3')"/>
</s:Group>
<!-- trace output on click: "button3", "button1", "button2" -->
This is very generic. I'd recommend using something more specific than UIComponent, otherwise it would probably dispatch more clicks than are necessary. That all depends on what you're actually doing.
Also, the MXML example is rather poor, since, if you knew all your components beforehand you could definitely handle it differently. However, if you had a bunch of components whose positions were determined at runtime, this would approach would make more sense.

Event listener to keyboard event not listening in a module

I am doing this inside a module containing viewstacks and their childs.
Calling onInit() on creationComplete of module.
When I am inside one of the childs of a viewstack of this module and press Enter, it doesnt not invoke the listener function at all (bp inside this does not get hit).
private function onInit():void{
this.addEventListener(KeyboardEvent.KEY_DOWN, keyPressed);
}
private function keyPressed(evt:KeyboardEvent):void
{//this breakpoint never gets hit on pressing a key in screen
if (evt.keyCode == Keyboard.ENTER)
{
//do this
}
}
You should add key listeners to stage objects:
private function onInit():void{
this.stage.addEventListener(KeyboardEvent.KEY_DOWN, keyPressed);
}
This can be very frustrating as there are a few different things that can affect this.
1) Add your event listeners at the appropriate place. The code that you have there is fine for capturing, just ensure it is in the parent or above of where the events are being fired.
2) You need to ensure you have focus. This is usually the issue people run into and it's in the docs but not immediately clear. If you look at the live doc link here and do a search for setFocus() - you'll notice that it is in every one of their examples (except top, which is broken!) - yet, they don't ever mention it in the actual documentation on the page.
http://livedocs.adobe.com/flex/3/html/help.html?content=events_11.html
So, even in their first example, if you click into the app (and not the textbox), it wont work!
<?xml version="1.0"?>
<!-- events/TrapAllKeys.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="initApp();">
<mx:Script><![CDATA[
private function initApp():void {
application.addEventListener(KeyboardEvent.KEY_UP, keyHandler);
}
private function keyHandler(event:KeyboardEvent):void {
t1.text = event.keyCode + "/" + event.charCode;
}
]]></mx:Script>
<mx:TextInput id="myTextInput"/>
<mx:Text id="t1"/>
</mx:Application>
If however, you set focus yourself by changing the init function like this, it will!
private function initApp():void {
application.addEventListener(KeyboardEvent.KEY_UP, keyHandler);
myTextInput.setFocus();
}
Another trick for testing if this is your problem is to add a textbox as a child of the container that has the capturing, if they magically work after you click in that text box - its a focus problem indeed!
=)