Flex : Problem with instantiating an mxml component - apache-flex

I have two mxml files in a flex project:
But when I trace a.cBtn, it is null.
Why should it be?
test.mxml :
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="runIt()">
<mx:Script>
<![CDATA[
public function runIt():void
{
var a:abc = new abc();
trace(a.cBtn);//a.cBtn is null here
}
]]>
</mx:Script>
</mx:Application>
And, abc.mxml :
<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" width="400" height="300">
<mx:Button x="108" y="73" label="Button" id="cBtn"/>
</mx:Canvas>

The underlying issue here is that in Flex, the children components of a given flex container component are not created until that container is initialized. The initialization process starts after you add the container to the display list. As noted above, the CREATION_COMPLETE event is fired after initialization is done and the children are instantiated, so you can safely access children at that point.
It's pretty ugly, but if you absolutely need to access the children of a component before you want to add that component to the display list, you can call "initialize()" on your container.
public function runIt():void
{
var a:abc = new abc();
trace(a.cBtn);//a.cBtn is null here
a.initialize();
trace(a.cBtn);//a.cBtn is not null here
}

You need to wait for the creationcomplete event.
public function runIt():void
{
var a:abc = new abc();
a.addEventListener(FlexEvent.CREATION_COMPLETE, traceIt)
trace(a.cBtn);//a.cBtn is null here
}
private function traceIt(e:Event):void
{
trace(a(e.target).cBtn)
}

Related

Where does "new" fit in the flex creation cycle?

In the following code, the call to myChild.bar() results in an exception because myChild is null. myParent is a valid object. What I don't understand is why myChild has not been created yet.
I have read the following document related to object creation sequence, but I am unsure how "new" is related:
http://livedocs.adobe.com/flex/3/html/help.html?content=layoutperformance_03.html
Any help is appreciated!
// Main.mxml
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="created()">
<mx:Script>
<![CDATA[
public var myParent:Parent = new Parent();
public function created():void {
myParent.foo();
}
]]>
</mx:Script>
</mx:Application>
// Parent.mxml
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="*">
<mx:Script>
<![CDATA[
public function foo():void {
myChild.bar();
}
]]>
</mx:Script>
<Child id="myChild"/>
</mx:Canvas>
// Child.mxml
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
public function bar():void {
trace("Hello World");
}
]]>
</mx:Script>
</mx:Canvas>
creationComplete fires when an object and all its child elements are created and drawn to the screen. Since your parent object is created with
public var myParent:Parent = new Parent();
it is not a child of your main object and the creationComplete event fires before myParent is initialized.
In fact, myParent.myChild will remain null until something causes myParent to initialize. You can cause this by adding it to a component on screen or you could just call myParent.initialize();
Flex is a visual display / UI framework. It is designed to keep a display list of UI items and handle various updates the items in the display list.
The problem is that you have never added your Parent component to the display list. This is done with the AddChild method in the Flex 2/3 Halo architecture or AddElement if you're using the Flex 4 Spark architecture.
Once you add the Parent component to the stage using the AddChild method, that component will start stepping through the component lifeCycle which includes creating it's Children (via createChildren() method), and sizing and positioning the children (via updateDisplayList() ). When defining a component and child via MXML--as an example your Parent.mxml file defines the Child class as a child using XML--the addChild method call is done "automagically" in the background.
Keep in mind that the Flex Component LifeCycle is a process and may not be immediate. If you perform an addChild on the parent; you may not be able to immediately access that parent's children on the next line.
So, the new keywords creates a new instance of the component; but it does not put that component onto the displayList for processing by the Flex Framework layout managers.
One way to rectify the situation might be this change to your main application file:
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="created()">
<mx:Script>
<![CDATA[
public function created():void {
myParent.foo();
}
]]>
</mx:Script>
<parent:Parent id="myParent" />
</mx:Application>
Another might be this:
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="created()">
<mx:Script>
<![CDATA[
public var myParent:Parent = new Parent();
public function created():void {
myParent.foo();
}
override protected function createChildren():void{
super.createChildren();
this.addChild(myParent);
}
]]>
</mx:Script>
</mx:Application>
For some good reading about this stuff, read through the Flex Component LifeCycle docs. http://livedocs.adobe.com/flex/3/html/ascomponents_advanced_2.html#204762

Why wouldn't a flex remoteobject be able to work within a custom component?

Please enlighten this flex noob. I have a remoteobject within my main.mxml. I can call a function on the service from an init() function on my main.mxml, and my java debugger triggers a breakpoint. When I move the remoteobject declaration and function call into a custom component (that is declared within main.mxml), the remote function on java-side no longer gets called, no breakpoints triggered, no errors, silence.
How could this be? No spelling errors, or anything like that. What can I do to figure it out?
mxml code:
&#060 mx:RemoteObject id="myService"
destination="remoteService"
endpoint="${Application.application.home}/messagebroker/amf" &#062
&#060 /mx:RemoteObject &#062
function call is just 'myService.getlist();'
when I move it to a custom component, I import mx.core.Application; so the compiler doesn't yell
my child component: child.mxml
<mx:Panel xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="init()" >
<mx:Script>
<![CDATA[
import mx.core.Application;
public function init():void {
helloWorld.sayHello();
}
]]>
</mx:Script>
<mx:RemoteObject id="helloWorld" destination="helloService" endpoint="$(Application.application.home}/messagebroker/amf" />
<mx:Label text="{helloWorld.sayHello.lastResult}" />
</mx:Panel>
my main.mxml:
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="init()" xmlns:test="main.flex.*" >
<mx:Script>
<![CDATA[
[Bindable]
public var home:String;
[Bindable]
public var uName:String;
public function init():void {
//passed in by wrapper html
home = Application.application.parameters.appHome;
uName = Application.application.parameters.uName;
}
]]>
</mx:Script>
<test:child />
</mx:Application>
The child components are calling creationComplete before the parent (so home is null). A solution is to throw an event (like InitDataCompleted) from the parent after you read the data, and in the child components listen for this event (so don't rely on creationcomplete in the child).
However more important than that is how can you diagnose in future this kind of problems. A simple tool like a proxy (eg Charles) should help.
For your endpoint value you've got
endpoint="$(Application.application.home}/messagebroker/amf"
Why are you using $( before Application.application... This should be a { as in:
endpoint="{Application.application.home}/messagebroker/amf"

Calling Application.application.enable from a TitleWindow in a different mxml component

I have a Flex RIA App, and in the application tag there is a button when it's pressed calls upon a TitleWindow from another .mxml file, and sets
application.enable = false
That way the user can't use any of the components in the application, and still can use the components in the TitleWindow.
The problem is when the TitleWindow is closed I want it to restore the application back to
application.enable = true
Which enables the application once again. But I can't call that code from inside the TitleWindow .mxml
How can I do it?
Here is the Source:
Loja.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" width="585" height="450" xmlns:ns1="com.*">
<mx:Style source="theme/simplicitygray.css" />
<mx:Script>
<![CDATA[
import mx.managers.PopUpManager;
private var clientid = 0;
public function openWindow() : void
{
if (clientid == 0)
{
PopUpManager.createPopUp(this,Login,false);
application.enabled = false;
} else {
PopUpManager.createPopUp(this,Conta,false);
application.enabled = false;
}
}
]]>
</mx:Script>
<mx:Panel x="10" y="40" width="565" height="400" layout="absolute">
</mx:Panel>
<mx:MenuBar x="10" y="10" width="565" height="22"></mx:MenuBar>
<mx:Button x="508" y="10" label="Aceder" click="openWindow();"/>
</mx:Application>
And one of the title windows. Once they are the same.
Login.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:TitleWindow xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" width="350" height="200" creationComplete="centerWindow()" showCloseButton="true" close="closeWindow()" title="Login">
<mx:Script>
<![CDATA[
import mx.managers.PopUpManager;
public function centerWindow():void
{
PopUpManager.centerPopUp(this);
}
public function closeWindow():void
{
PopUpManager.removePopUp(this);
}
]]>
</mx:Script>
</mx:TitleWindow>
application is a static property of the Application class and can be called from the TitleWindow
public function closeWindow():void
{
PopUpManager.removePopUp(this);
Application.application.enabled = true;
}
BTW, There is another easier way to achieve the following:
That way the user cant use any of the components in the application, and still can use the components in the TitleWindow.
That is to use a modal popup. Set the third parameter of the createPopUp to true and that's it - you don't have to enable/disable the application manually: flex will take care of it.
PopUpManager.createPopUp(this,Login, true);
application will automatically become functional once you call removePopUp.
You can use custom events to enable this functionality, as described here.
Essentially, you set up a custom event in the class you are calling, then create a function that runs when the event is consumed. That way your 'Loja' will know when the 'Login' is done.

Passing a value from one MXML to another

I am getting a value in a MXML... now i need to pass it to another MXML to invoke an event... how can i do it.
It can be done like this
Test.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
<mx:Script>
<![CDATA[
public var a:String;
]]>
</mx:Script>
</mx:Application>
Test2.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
<mx:Script>
<![CDATA[
public var a1:String;
public var te1:Test=new Test();
public function init():void{
a1=te1.a;
}
]]>
</mx:Script>
</mx:Application>
this is not right i think but it may serve your purpose
Assuming one MXML component is the child of the other, you should be using binding to pass data around.
You could dispatch an event containing the string value from the source component to be received by the destination component.
You need to explain more about how your two mxml components relate to each other... parent/child? two siblings within a parent? That will determine the best approach. Of course, your components should not really be "wired into each other" if possible, which is where frameworks such as Mate come in, but that is probably way beyond where you're at just now.

How do I ensure a Flex dataProvider processes the data synchronously?

I am using an component, and currently have a dataProvider working that is an ArrayCollection (have a separate question about how to make this an XML file... but I digress).
Variable declaration looks like this:
[Bindable]
private var _dpImageList : ArrayCollection = new ArrayCollection([
{"location" : "path/to/image1.jpg"},
{"location" : "path/to/image2.jpg"},
{"location" : "path/to/image3.jpg"}
]);
I then refer to like this:
<s:List
id="lstImages"
width="100%"
dataProvider="{_dpImageList}"
itemRenderer="path.to.render.ImageRenderer"
skinClass="path.to.skins.ListSkin"
>
<s:layout>
<s:HorizontalLayout gap="2" />
</s:layout>
</s:List>
Currently, it would appear that each item is processed asynchronously.
However, I want them to be processed synchronously.
Reason: I am displaying a list of images, and I want the leftmost one rendered first, followed by the one to its right, and so on.
Edit:
I just found this answer.
Do you think that could be the same issue?
Instead of declaring the variable and using it as the binding source, declare two collections. Then, onCreationComplete call loadNext() which shifts an object out of the second array and pushes it into the first. When the item has been rendered (custom event dispatched by itemRenderer and caught) call loadNext() again until such time as your source array is empty and your bound dataProvider has all the images.
I can write it in code if this doesn't make any sense. ;)
<?xml version="1.0" encoding="utf-8"?>
<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/halo" minWidth="1024" minHeight="768" creationComplete="init()">
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
[Bindable]
private var _source : ArrayCollection = new ArrayCollection([
{"location" : "path/to/image1.jpg"},
{"location" : "path/to/image2.jpg"},
{"location" : "path/to/image3.jpg"}
]);
[Bindable] private var dataProvider:ArrayCollection;
protected function init():void
{
this.lstImages.addEventListener( "imageLoaded", handleImageLoaded);
loadImage()
}
protected function loadImage():void
{
if(this._source.length<=0)
return;
var image:Object = this._source.getItemAt(0);
dataProvider.addItem(image);
this._source.removeItemAt(0);
}
protected function handleImageLoaded(event:Event):void
{
loadImage()
}
]]>
</fx:Script>
<s:List
id="lstImages"
width="100%"
dataProvider="{_dpImageList}"
itemRenderer="path.to.render.ImageRenderer"
skinClass="path.to.skins.ListSkin"
>
<s:layout>
<s:HorizontalLayout gap="2" />
</s:layout>
</s:List>
</s:Application>
Your item renderer's image's complete handle will dispatch:
protected function handleImageLoaded(event:Event):void
{
owner.dispatch(new Event("imageLoaded"));
}
And that should load your images in a clean sequence.

Resources