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

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

Related

Flash builder 4.6 - code behind approach

I'm trying to figure out the right approach for "Code behind" using flash builder for a mobile app:
I'm creating a flex mobile AIR project (Based on the "Tabbed view" template)
setting my UI in design mode
now I want all the logic to be in a separate class that will change the UI look accordingly
Sounds easy, however I can't really get the approach for doing it, any help is appreciated :)
Update:
main app:
<?xml version="1.0" encoding="utf-8"?>
<s:TabbedViewNavigatorApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark" applicationDPI="160">
<s:ViewNavigator label="a" width="100%" height="100%" firstView="views.aView"/>
<s:ViewNavigator label="b" width="100%" height="100%" firstView="views.bView"/>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
</s:TabbedViewNavigatorApplication>
view A:
<?xml version="1.0" encoding="utf-8"?>
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark" title="a">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<s:Label id="txt" x="280" y="139" text="Label"/>
</s:View>
So now I want MyClass to change txt textField according to my logic, what is the right approach?
An elegant way would be implementing IMXMLObject. When implementing this interface, the IMXMLObject#initialize method will take the component (named document of type Object) and an optional id (of type String) as arguments and u can easily implement this pattern. The big advantage is, that you use composition over inheritance and when using interfaces, you can use it as some sort of type save mix-in as view behavior:
package net.icodeapp.examples.views
{
import flash.events.MouseEvent;
import mx.core.IMXMLObject;
import mx.events.FlexEvent;
public class ViewBaseModel implements IMXMLObject
{
//-------------------------------------------------------------------------
//
// Properties
//
//-------------------------------------------------------------------------
private var _id:String;
private var _viewBase:ViewBase;
protected function set viewBase(value:ViewBase):void
{
_viewBase = value;
if (!_viewBase)
throw new ArgumentError('View must be instance of ViewBase');
if (!_viewBase.initialized)
_viewBase.addEventListener(FlexEvent.CREATION_COMPLETE, viewBase_creationCompleteHandler, false, 0, true);
else
viewCreationCompleted();
}
//-------------------------------------------------------------------------
//
// Constructor
//
//-------------------------------------------------------------------------
public function ViewBaseModel()
{
}
//-------------------------------------------------------------------------
//
// Methods
//
//-------------------------------------------------------------------------
public function initialized(document:Object, id:String):void
{
viewBase = document as ViewBase;
_id = id;
}
private function viewCreationCompleted():void
{
_viewBase.addEventListener(MouseEvent.CLICK, viewBase_clickHandler);
}
//-------------------------------------------------------------------------
//
// Event Handler
//
//-------------------------------------------------------------------------
private function viewBase_creationCompleteHandler(event:FlexEvent):void
{
viewCreationCompleted();
}
private function viewBase_clickHandler(event:MouseEvent):void
{
// todo: do some action
}
}
}
The model is initialized and references are set by the framework. When taking a peek at the generated ActionScript code you'll see, that IMXMLObject#initialize it called in the constructor after the model is instantiated.
<?xml version="1.0"?>
<s:Group xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:views="net.icodeapp.examples.views.*">
<fx:Declarations>
<views:ViewBaseModel/>
</fx:Declarations>
</s:Group>
The model would receive by events by the view and can call methods on it.
All you do is make an AS file that has the same base class as whatever your MXML object was initially set up as, for example if it's a VGroup make MyBaseClass extends VGroup, then change the VGroup to MyBaseClass.
Example
[Main.mxml]
<main:MainBase
xmlns:main="*"
...>
</main:MainBase>
[MainBase.as]
public class MainBase extends Application
Think of your Code Behind as a base class (or an Abstract Class). In an Abstract Class, it is really common for the actual implementation of methods or the "real objects" behind properties to be left to the extending class(es) to supply.
This is exactly like what you do when you set a base class in Flash to your custom Class, but the actual member objects (buttons, etc.) are provided on the stage of the MovieClip whose library instance is linked to your clip.
For more on code behind, check out my blog post here. If you'd like to check out the code for the template component described there, look here. Though template components are less useful in the Spark world (IMO).

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"

Flex : Problem with instantiating an mxml component

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)
}

Using a composite MXML component from ActionScript

I'm trying componentize one of the pieces of UI in an AIR application that I'm developing in Flex. In this example, I want to display file information on a single line (which has an icon, some text/link and the size).
My code looks like this (component is called FileDisplay):
<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
public function set iconType(source:String):void {
this.ficon.source = source;
}
public function set fileName(name:String):void {
this.fname.htmlText = name;
}
public function set fileSize(size:String):void {
this.fsize.text = size;
}
]]>
</mx:Script>
<mx:Image id="ficon" />
<mx:Label id="fname" left="20" right="30" text="Filename" />
<mx:Label id="fsize" right="0" text="0 K" />
</mx:Canvas>
When I'm using this component in my main application, the actionscript looks like:
for each (var file:XML in result.files) {
var fd:FileDisplay = new FileDisplay();
fd.fileName = ''+file.name+'';
fd.iconType = getFileTypeIcon(file.name);
fd.fileSize = getFileSizeString(file.size);
this.file_list.addChild(fd);
}
However, when I do this, I get an error: Error #1009: Cannot access a property or method of a null object reference. This is because the child components of the FileDisplay are null (or at least they show up that way in the debugger).
Does anyone know if there's a way around this? Am I supposed to be waiting for events indicating the child components were created? Is there a more common pattern that solves this problem?
For now I can manually do everything in ActionScript in my main app (create a Canvas and add children to it) but I would appreciate any insight on how to separate the code more cleanly.
Bindable to the rescue:
<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
[Bindable]
public var iconType:String;
[Bindable]
public var fileName:String = "Filename";
[Bindable]
public var fileSize:String = "0 K";
]]>
</mx:Script>
<mx:Image id="ficon" source="{iconType}"/>
<mx:Label id="fname" left="20" right="30" text="{fileName}" />
<mx:Label id="fsize" right="0" text="{fileSize}" />
</mx:Canvas>
the values will be automatically updated when the components are created.
The subcomponents haven't been loaded yet.
Read this: http://livedocs.adobe.com/flex/3/html/help.html?content=ascomponents_advanced_2.html#203434.
Then, when like me, you don't understand it (and it's not reliable), listen for the FlexEvent.CREATION_COMPLETE within FileDisplay, and apply your child component properties there.
Or better yet, create the three children programmatically in the "createChildren" function, and apply the settings there.
Both of these methods assume that you're setting filename, icontype, and filesize as local members before applying them to the children components, which you should be doing regardless.
What is the parent component that holds the FileDisplay component? If you're sure that the error is coming from the fact that the child components of FileDisplay aren't being instantiated then you might want to look at the creationPolicy attribute and make sure it's set to ContainerCreationPolicy.ALL on that parent component.
=Ryan
In addition to setting the CreationPolicy to all, you need to add the DisplayObject to the stage via addChild. The children of FileDisplay are not created until you add it is added to the stage. So do:
for each (var file:XML in result.files) {
var fd:FileDisplay = new FileDisplay();
this.file_list.addChild(fd);
fd.fileName = ''+file.name+'';
fd.iconType = getFileTypeIcon(file.name);
fd.fileSize = getFileSizeString(file.size);
}

setting content area in custom flex component

I am trying to define the content area of a custom component that extends mx:VBox. The component has predefined headers and footers (visual children) and I want to set the area in the middle for adding children to the component. The component will be used like this:
<custom_component>
<mx:button/>
</custom_component>
How would I set this content area?
There's actually a few steps to it.
Your custom component needs to set its DefaultProperty metadata so the children don't collide with the ones within the custom component itself.
Then you need to stow them away in an instance var to add to your content area later, because the properties will be set before the components children are created.
Lastly, if multiple children are specified your DefaultProperty will be handed an Array object (rather than a single UIComponent instance.)
So your custom component would look something like this:
<?xml version="1.0" encoding="utf-8"?>
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml" width="400" height="300">
<mx:Metadata>
[DefaultProperty("content")]
</mx:Metadata>
<mx:HBox id="headerBox"/>
<mx:VBox id="contentBox"/>
<mx:HBox id="footerBox"/>
<mx:Script>
<![CDATA[
import mx.core.UIComponent;
private var _contentChildren:Array;
public function set content(c:*) : void {
// Allow 1 or more children to be specified
_contentChildren = (c as Array) || [c];
}
override protected function createChildren() : void {
// Call super so contentBox gets created first
super.createChildren();
for each (var child:UIComponent in _contentChildren) {
contentBox.addChild(child);
}
}
]]>
</mx:Script>
</mx:VBox>
In your custom component, add the DefaultProperty metadata tag:
[DefaultProperty("nameOfDefaultProperty")]
Then you would also define a setter for that property:
public function set nameOfDefaultProperty(value:UIComponent):void
{
if (value != null)
{
// add "value" to the display list here
}
}
Try using a canvas?

Resources