TextArea component is null on applicationComplete event - apache-flex

I have a weird issue (weird because it is specific to one component) with applicationComplete in a fairly simple application. All the UI components are declared in MXML. I can access them all in applicationComplete, but not a spark.components.TextArea component, named taStatus here; it is null in the handler.
MXML looks sort of like this (there are lots of other components, but nothing special)
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="710" minHeight="640" applicationComplete="onApplicationComplete(event)" width="710" height="640">
<mx:TabNavigator left="15" right="15" top="15" bottom="340" paddingTop="0">
<s:NavigatorContent label="General" width="100%" height="100%">
<s:Label x="93" y="71" text="Label" id="lblTest"/>
</s:NavigatorContent>
<s:NavigatorContent label="Status" width="100%" height="100%">
<s:TextArea id="taStatus" width="100%" height="100%" text="Startup." editable="false"/>
</s:NavigatorContent>
</mx:TabNavigator>
<fx:Script source="main.as" />
</s:Application>
Here is the handler in main.as
protected function onApplicationComplete(event: FlexEvent) : void
{
lblTest.text = 'abc789'; // OK
taStatus.text = 'abc789'; // Fail
}
TypeError: Error #1009: Cannot access a property or method of a null object reference. So taStatus is null... What is so special about this TextArea?
Update 2010-06-12 02:53
Moving the NavigatorContent (tab) above all other tabs suddenly makes the TextAreas get instantiated on time. Very strange, because all the components are definitely being created; I can see them.

It's because the TextArea is in a child of the TabNavigator that is not the first child, so by default it is not instantiated until the user opens that tab.
Your options are to either wait until the user opens that tab to do whatver you need to do to set up the TextArea or change the child creation policy on the TabNavigator to create all its children at startup rather than waiting for them to be clicked.
To do that, you need to set the creationPolicy property on the TabNavigator to "all".

Related

How to create and manage a nested TabbedViewNavigator?

I'm trying to implement a nested TabbedViewNavigator (inside a View). I did this by following method 3 described in this post:
Flex - how to make a tabbed panel
Code here:
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark" title="HomeView">
<s:TabbedViewNavigator width="100%" height="100%">
<s:ViewNavigator label="1st Tab" width="100%" height="100%"
firstView="views.FirstTabView"/>
<s:ViewNavigator label="2nd Tab" width="100%" height="100%"
firstView="views.SecondTabView"/>
<s:ViewNavigator label="3rd Tab" width="100%" height="100%"
firstView="views.ThirdTabView"/>
</s:TabbedViewNavigator>
However if I call "navigator.pushView(someView)" inside one of the child views - let's say FirstTabView - the new view will be pushed into the nested TabbedViewNavigator instead of the parent's view navigator. That is not what I want. I want to completely change the application state and show a new view. How can I achieve that? Should I listen (in the main view) for changes within the nested TabbedViewNavigator and then push the new view from there? Or should I just do this some other way?
I solved it by using a custom event (it's custom because I need to get data from it).
Added a listener and a handler to the TabbedViewNavigator. The event is dispatched from inside the active ViewNavigator view. The event has bubbles=true so that it goes up to the top of the event chain. Then just push the new view in the event handler.
Main View:
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark" title="HomeView">
(...)
private function setup(){
tabbed_panel.addEventListener(CustomEvent.BUTTON_PRESSED, eventHandler);
}
private function eventHandler(ev:CustomEvent):void{
navigator.pushView(MyNewView,ev.event_data);
}
(...)
<s:TabbedViewNavigator id="tabbed_panel" width="100%" height="100%">
<s:ViewNavigator label="1st Tab" width="100%" height="100%"
firstView="views.FirstTabView"/>
<s:ViewNavigator label="2nd Tab" width="100%" height="100%"
firstView="views.SecondTabView"/>
</s:TabbedViewNavigator>
(...)
</s:View>
FirstTabView:
<View...>
(...)
private function buttonClickedHandler(event:Event):void{
dispatchEvent(new CustomEvent(event_data,"BUTTON_PRESSED",true));
}
(...)
</View>
I don't know if this is a corrrect approach to the problem, but it works.

Switch between Flex Tabbed ViewNavigators

I am working on a Flex TabbedViewNavigatorApplication with three tabs (ViewNavigator elements). I would like to switch from one ViewNavigator to another based upon a user action (via ActionScript code).
I know that switching between Views uses pushView and popView, but I'm working with ViewNavigators, and my searching revealed nothing useful.
I'm trying to switch from Tab2 to Tab1 when an event occurs. In this case, Tab2 contains a list, and when the user makes a selection, I want to jump back to Tab1.
<s:TabbedViewNavigatorApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
creationComplete="onAppReady(event)">
<s:ViewNavigator label="Tab1" width="100%" height="100%" firstView="views.TabOneView"/>
<s:ViewNavigator label="Tab2" width="100%" height="100%" firstView="views.TabTwoView"/>
<s:ViewNavigator label="Tab3" width="100%" height="100%" firstView="views.TabThreeView"/>
</s:TabbedViewNavigatorApplication>
Thanks for your help!
I use the following line of ActionScript to switch from one ViewNavigator to another based on a user action:
TabbedViewNavigator(navigator.parentNavigator).selectedIndex = 1;
It worked like a charm and seems simpler than bubbling events.
This class is strangely undocumented. I have not tried this myself, but from searching online, this is what I found which corroborates with what the rest of the network does.
What you need to do is bubble an event to the TabbedViewNavigatorApplication and from there change the selectedIndex property to whichever tab you need to change to. For example:
<s:TabbedViewNavigatorApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
creationComplete="onCreationComplete()">
<fx:Script>
<![CDATA[
private function onCreationComplete():void
{
this.addEventListener('someEvent', someHandler);
}
private function someHandler(e:Event):void
{
this.selectedIndex = 0; // or whatever index you want.
}
]]>
</fx:Script>
<s:ViewNavigator label="Tab1" width="100%" height="100%" firstView="views.TabOneView"/>
<s:ViewNavigator label="Tab2" width="100%" height="100%" firstView="views.TabTwoView"/>
<s:ViewNavigator label="Tab3" width="100%" height="100%" firstView="views.TabThreeView"/>
</s:TabbedViewNavigatorApplication>
You just need to dispatch a bubbling event from within your children. You could event create a custom event that holds data about which tab to switch to.

spark List with ItemRenderer click function not working

I am having an issue with my ItemRenderer, which I am using for a spark List. My code is the following:
I have this list:
<s:List
id="productSetList"
dataProvider="{ model.productSets }"
change="model.selectSet( productSetList )"
height="100%" width="100%"
borderVisible="false"
itemRenderer="SideBarItemRenderer" top="20" left="15">
</s:List>
and my itemRenderer is:
<s:ItemRenderer
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/halo"
width="160" height="175" autoDrawBackground="false" buttonMode="true" useHandCursor="true"
click="click(event)" cacheAsBitmap="true"
>
<fx:Script>
<![CDATA[
import com.png.vm.model.vos.ProductSet;
protected function click(event:MouseEvent):void
{
trace('arthur');
}
]]>
</fx:Script>
<s:BitmapImage source="{ProductSet(data).image}" width="160" height="175"/>
</s:ItemRenderer>
The thing is, if I scroll the list, and click on an item, it does not trace 'arthur' ! Why is this so ? I must trace that all the time that someone clicks in the list!
EDIT:
If I remove change="model.selectSet( productSetList )" in the list, it works!! but I cannot remove that, some suggestions ? How can I switch that to another function?
Trace only works when you are debugging/using the debugging version of Flash Player. Make sure you are using that. If you want a pop-up message use Alert.show("message")
For more information about trace() check out:
http://livedocs.adobe.com/flex/3/html/help.html?content=logging_08.html
And Alert.show(): http://help.adobe.com/en_US/AS2LCR/Flash_10.0/help.html?content=00001965.html
If you are running debug player. Try originating the click event from an embedded <s:Group> this way whatever you add in here beyond the bitmap will still trigger the click event.
Something like:
<s:Group width="100%" height="100%" click="click(event)" >
<s:BitmapImage source="{ProductSet(data).image}" width="160" height="175"/>
</s:Group>
I've definitely had click events work for me inside of ItemRenderers before
My apologies, I have finally solved it. The problem was that inside the function,model.selectSet, I was calling List.change; I was messing the list up! My function was as follows:
public function selectSet(list:List):void {
list.layout.verticalScrollPosition=100;
// another stuffs
}
So, I just removed the line : list.layout.verticalScrollPosition=100; and now it`s working fine.
Thanks for all the help!

Capturing key events in a mx:Image

I'm trying to capture key events in a mx:Image and I can't get it to work.
<?xml version="1.0" encoding="utf-8" ?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" />
<mx:Canvas width="100%" height="100%">
<mx:Label id="lab" x="50" y="20" text="Nothing"/>
<mx:Image x="50" y="50" source="#Embed('image.png')" keyDown="lab.text='Something';"/>
</mx:Canvas>
</mx:Application>
When I press a key when the mouse is over the image I expect the label text to change to "Something", but nothing happens. I've done all sorts of combination of enabled and putting the keyDown on the canvas and the label as well.
What am I missing?
The issue is one of focus. Key down events are only generated within a component when that component (or one of its descendants) has focus. In order for an Image to receive focus, you must set focusEnabled to true. This, however, requires that the user tab to give the Image focus, since clicking on an Image does not convey focus, much less mousing over it.
If you want to listen for the key down event when the user's mouse is over the Image, you can manually assign focus to the Image when the user moves their mouse over it.
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
<mx:Script>
<![CDATA[
import mx.core.UIComponent;
import mx.managers.IFocusManagerComponent;
private var oldFocus:IFocusManagerComponent;
public function imageMouseOver(event:MouseEvent):void {
oldFocus = focusManager.getFocus();
var newFocus:UIComponent = event.target as UIComponent;
newFocus.setFocus();
}
private function imageMouseOut(event:MouseEvent):void {
if (oldFocus) {
oldFocus.setFocus();
}
}
]]>
</mx:Script>
<mx:Canvas width="100%" height="100%">
<mx:Label id="lab" x="50" y="20" text="Nothing"/>
<mx:Image x="50" y="50" source="#Embed('image.png')" mouseOver="imageMouseOver(event)" mouseOut="imageMouseOut(event)" keyDown="lab.text='Something';" focusEnabled="true"/>
</mx:Canvas>
</mx:Application>
Alternately, you can assign a key down listener to the stage when the user mouses over the Image, and remove it when the user mouses out.
Image derives from (a.k.a "is a") SWFLoader. You need to add listeners to the content of the loader, not the loader itself. See this question for details, Interact with SWF Loader

Binding custom components values

I have built a custom component using some containers and a TileList.
Now when I instantiate that component in my main Flex app, I want to get the value of the selected item in the tileList that the user clicks on. In other words, everytime the user clicks an item in the tileList, I want it to assign that selected value to a global application variable in the main flex app.
Any ideas how to do that?
Below is one way that you can listen to the change of TileList.selectedItem. I would recommend against putting this in a global variable, although if you must you could use a pattern like ModelLocator to do so.
<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
layout="vertical">
<mx:Script>
<![CDATA[
[Bindable] public var selectedItem:Object;
]]>
</mx:Script>
<mx:Binding source="listTile.selectedItem" destination="selectedItem"/>
<mx:Label text="{ selectedItem }"/>
<mx:TileList
id="listTile"
width="400"
height="300"
dataProvider="{ ['A','B','C'] }"/>
</mx:Application>

Resources