Turning Components From MXML To Actionscript – Part 3
We ended the last section at an intermediate level of difficulty, by creating our own custom component in MXML with custom events, binding, and metadata. Now we are going into the advanced section. We will turn all of that MXML into Actionscript.
But first, let’s chat. Some of the reasons for making this laborious change is because we want to use asdocs on our components, we want to have smaller components that are as light as possible, we want to remove some of the overhead of binding, and honestly we are perfectionists. There are other reasons I am sure, but those are my reasons. When this blog post is complete we will have our MXML component’s Actionscript twin.
Let us start by creating our Actionscript file and then breaking down the MXML component architecture.
First, create the component.

And set the properties in the new Actionscript class.

Our Actionscript component is simple right now, but is about to become big fast. The initial state should look like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 | package com.unitedmindset.components { import mx.containers.Panel; public class ComponentAS extends Panel { public function ComponentAS() { super(); } } } |
Now we need to start transferring code from the MXML component to the Actionscript component. To do this we have to have a working understanding of the component lifecycle in Flex. When a component is added to a view the component goes through the following set up (in order).
1 2 3 4 5 6 | Constructor called Sets Parent Computer Styles Settings Preintialization event createChildren called calls invalidateProperties, invalidateSize, invalidateDisplayList to trigger commitProperties, measure, updateDisplayList later |
The component stops here, until the addChild function is called on the component and then the remainder of the component lifecycle is invoked, in the following order:
1 2 3 4 5 6 7 8 9 10 | Initialize event childAdd event on Parent Initialize event on Parent Render event calls commitProperties, measure, layoutChrome, updateDisplayList updateComplete event calls commitProperties, measure, layoutChrome, updateDisplayList if the previous calls invoked the invalidateProperties, invalidateSize, invalidateDisplayList functions visible set to true CreationComplete event updateComplete event is fired whenever a change is made to the component |
With this knowledge we know the order to move information from the MXML component to the Actionscript Component.
Step 1:
The first section in an MXML component is equivalent to setting parameters in the constructor of the Actionscript (AS) component. The first block that I am referencing is the following section:
1 2 3 | <mx:Panel xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" width="100%" height="100%"> |
Taking that information to the AS Component we get the following:
1 2 3 4 5 6 7 8 9 10 11 12 | //-------------------------- // // Constructor // //-------------------------- public function ComponentAS() { super(); this.layout = "vertical"; this.percentHeight = 100; this.percentWidth = 100; } |
Step 2:
Next let’s focus on creating the children of our component. You will remember that the MXML looked like this:
1 2 3 4 5 6 7 8 | <mx:DataGrid width="100%" height="100%" columns="{this.columns}" id="dataGrid"/> <mx:ControlBar> <mx:Button label="{this.label1}" click="this._button1Click_Handler(event)" id="button1"/> <mx:Spacer width="100%"/> <mx:Button label="{this.label2}" click="this._button2Click_Handler(event)" id="button2"/> <mx:Spacer width="100%"/> <mx:Button label="{this.label3}" click="this._button3Click_Handler(event)" id="button3"/> </mx:ControlBar> |
In Actionscript we need to take some steps to ensure that our code checks for errors, checks that MXML provides for us. You will see these checks in the following code. One of the most important checks to preform is that the object you are creating is null before you create it. We perform this check so if another coder extends your component and accidentally calls the super.createChildren() twice, you won’t will end up with double components being added to your component. Also, remember to always call the super class before doing anything.
One thing you will notice is that the controlBar already exists in the panel component, so we just need to initialize it, not add in another.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | //-------------------------- // // Children Components // //-------------------------- public var dataGrid:DataGrid; public var button1:Button; public var button2:Button; public var button3:Button; private var _spacer1:Spacer; private var _spacer2:Spacer; //-------------------------- // // Overrides // //-------------------------- override protected function createChildren():void { // call super class createChildren() super.createChildren(); // if the dataGrid doesn't exist, create dataGrid if(!dataGrid){ dataGrid = new DataGrid(); dataGrid.percentHeight = 100; dataGrid.percentWidth = 100; this.addChild(dataGrid); } // if controlBar doesn't exist, create the controlBar if(!this.controlBar){ this.controlBar = new ControlBar(); //add control bar this.addChildAt(ControlBar(this.controlBar),this.numChildren); this.createComponentsFromDescriptors(); } // if the button doesn't exist, create button if(!button1){ button1 = new Button(); ControlBar(this.controlBar).addChild(button1); } // if the spacer doesn't exist, create spacer if(!_spacer1){ _spacer1 = new Spacer(); _spacer1.percentWidth = 100; ControlBar(this.controlBar).addChild(_spacer1); } // if the button doesn't exist, create button if(!button2){ button2 = new Button(); ControlBar(this.controlBar).addChild(button2); } // if the spacer doesn't exist, create spacer if(!_spacer2){ _spacer2 = new Spacer(); _spacer2.percentWidth = 100; ControlBar(this.controlBar).addChild(_spacer2); } // if the button doesn't exist, create button if(!button3){ button3 = new Button(); ControlBar(this.controlBar).addChild(button3); } } |
So far so good, if you check the code that we have now everything appears to be working just the way we want. Now it is time to start adding in the properties, events, and meta data.
Step 3:
Now we create your properties and override the commitProperties protected function to update the component as a change is made. We will use one general theory when making this refactor, the theory is that this…
1 2 | [Bindable] public var label1:String = "Button"; |
…becomes this:
1 2 3 4 5 6 7 8 9 10 11 12 | private var _label1:String; private var _label1Changed:Boolean = false; public function get label1():String { return this._label1; } public function set label1(value:String):void { this._label1 = value; this._label1Changed = true; this.invalidateProperties(); } |
… with this added into the commitProperties function:
1 2 3 4 5 6 | // label 1 if(_label1Changed) { _label1Changed = false; this.button1.label = label1; } |
We need to go back to where our button is being created and add the label to the button on instantiation.
1 2 3 4 5 6 | // if the button doesn't exist, create button if(!button1){ button1 = new Button(); button1.label = this.label1; ControlBar(this.controlBar).addChild(button1); } |
Step 4:
Now we are going to add in the custom events and functions that we had from the MXML component, this part should just be a copy and paste – we will also move over the defaultProperty MetaData tag with the event tags into the head of your Actionscript file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | package com.unitedmindset.components { import flash.events.Event; import flash.events.MouseEvent; import mx.containers.ControlBar; import mx.containers.Panel; import mx.controls.Button; import mx.controls.DataGrid; import mx.controls.Spacer; [DefaultProperty("columns")] [Event(type="flash.events.Event",name="button1Click")] [Event(type="flash.events.Event",name="button2Click")] [Event(type="flash.events.Event",name="button3Click")] public class ComponentAS extends Panel {... |
Add in the listener methods….
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | //-------------------------- // // listeners // //-------------------------- private function _button1Click_Handler(event:MouseEvent):void { dispatchEvent(new Event("button1Click")); } private function _button2Click_Handler(event:MouseEvent):void { dispatchEvent(new Event("button2Click")); } private function _button3Click_Handler(event:MouseEvent):void { dispatchEvent(new Event("button3Click")); } |
And don’t forget to add the event listeners to the buttons…
1 2 3 4 5 6 7 | // if the button doesn't exist, create button if(!button1){ button1 = new Button(); button1.label = this.label1; button1.addEventListener(MouseEvent.CLICK,this._button1Click_Handler); ControlBar(this.controlBar).addChild(button1); } |
Step 5:
Though this example does not require any layout help, you would use the updateDisplayList at this point to be able layout our component.
Step X:
Any steps beyond this point are up to your and your component. Do you need to add more methods? Listeners? Styles? If you do now would be the time to add in this code, but for my example the Actionscript code does everything that my MXML code does.
The final Actionscript component is below with full asdocs.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 | package com.unitedmindset.components { import flash.events.Event; import flash.events.MouseEvent; import mx.containers.ControlBar; import mx.containers.Panel; import mx.controls.Button; import mx.controls.DataGrid; import mx.controls.Spacer; /** * Default Property sets the columns */ [DefaultProperty("columns")] /** * Event Triggered when the 1st Button is clicked */ [Event(type="flash.events.Event",name="button1Click")] /** * Event Triggered when the 2nd Button is clicked */ [Event(type="flash.events.Event",name="button2Click")] /** * Event Triggered when the 3rd Button is clicked */ [Event(type="flash.events.Event",name="button3Click")] /** * Custom Panel with 3 buttons and a DataGrid. * * <p> * Useage: * </p> * <pre> * <components:ComponentAS label1="Label 1" label2="Label 2" label3="Label 3" title="AS Component" status="working"> * <mx:DataGridColumn headerText="hello"/> * <mx:DataGridColumn headerText="world"/> * </components:ComponentAS> * </pre> * @author Administrator * */ public class ComponentAS extends Panel { //-------------------------- // // Constructor // //-------------------------- public function ComponentAS() { super(); this.layout = "vertical"; this.percentHeight = 100; this.percentWidth = 100; } //-------------------------- // // Children Components // //-------------------------- public var dataGrid:DataGrid; public var button1:Button; public var button2:Button; public var button3:Button; private var _spacer1:Spacer; private var _spacer2:Spacer; //-------------------------- // // createChildren // //-------------------------- override protected function createChildren():void { // always call super class createChildren() super.createChildren(); // if the dataGrid doesn't exist, create dataGrid if(!dataGrid){ dataGrid = new DataGrid(); dataGrid.percentHeight = 100; dataGrid.percentWidth = 100; this.addChild(dataGrid); } // if controlBar doesn't exist, create the controlBar if(!this.controlBar){ this.controlBar = new ControlBar(); //add control bar this.addChildAt(ControlBar(this.controlBar),this.numChildren); this.createComponentsFromDescriptors(); } // if the button doesn't exist, create button if(!button1){ button1 = new Button(); button1.label = this.label1; button1.addEventListener(MouseEvent.CLICK,this._button1Click_Handler); ControlBar(this.controlBar).addChild(button1); } // if the spacer doesn't exist, create spacer if(!_spacer1){ _spacer1 = new Spacer(); _spacer1.percentWidth = 100; ControlBar(this.controlBar).addChild(_spacer1); } // if the button doesn't exist, create button if(!button2){ button2 = new Button(); button2.label = this.label2; button2.addEventListener(MouseEvent.CLICK,this._button2Click_Handler); ControlBar(this.controlBar).addChild(button2); } // if the spacer doesn't exist, create spacer if(!_spacer2){ _spacer2 = new Spacer(); _spacer2.percentWidth = 100; ControlBar(this.controlBar).addChild(_spacer2); } // if the button doesn't exist, create button if(!button3){ button3 = new Button(); button3.label = this.label3; button3.addEventListener(MouseEvent.CLICK,this._button3Click_Handler); ControlBar(this.controlBar).addChild(button3); } } //-------------------------- // // public properties // //-------------------------- //label 1 private var _label1:String; private var _label1Changed:Boolean = false; /** * Button 1 label */ public function get label1():String { return this._label1; } public function set label1(value:String):void { this._label1 = value; this._label1Changed = true; this.invalidateProperties(); } //label 2 private var _label2:String; private var _label2Changed:Boolean = false; /** * Button 2 label */ public function get label2():String { return this._label2; } public function set label2(value:String):void { this._label2 = value; this._label2Changed = true; this.invalidateProperties(); } //label 3 private var _label3:String; private var _label3Changed:Boolean = false; /** * Button 3 label */ public function get label3():String { return this._label3; } public function set label3(value:String):void { this._label3 = value; this._label3Changed = true; this.invalidateProperties(); } //columns private var _columns:Array; private var _columnsChanged:Boolean = false; public function get columns():Array { return this._columns.concat(); } public function set columns(value:Array):void { this._columns = value; this._columnsChanged = true; this.invalidateProperties(); } //-------------------------- // // commit properties // //-------------------------- override protected function commitProperties():void { // always call super class commitProperties() super.commitProperties(); // label 1 if(_label1Changed) { _label1Changed = false; this.button1.label = label1; } // label 2 if(_label2Changed) { _label2Changed = false; this.button2.label = label2; } // label 3 if(_label3Changed) { _label3Changed = false; this.button3.label = label3; } // columns if(_columnsChanged) { _columnsChanged = false; this.dataGrid.columns = this.columns; } } //-------------------------- // // listeners // //-------------------------- /** * @private * Button 1 Click Handler * @param event * */ private function _button1Click_Handler(event:MouseEvent):void { dispatchEvent(new Event("button1Click")); } /** * @private * Button 2 Click Handler * @param event * */ private function _button2Click_Handler(event:MouseEvent):void { dispatchEvent(new Event("button2Click")); } /** * @private * Button 3 Click Handler * @param event * */ private function _button3Click_Handler(event:MouseEvent):void { dispatchEvent(new Event("button3Click")); } } } |
Wow, lots of code. Well let us finish up by looking back at our application. The same exact options are set in MXML like they would be if the component were made in MXML.
1 2 3 4 5 6 7 8 9 10 11 | <?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" xmlns:components="com.unitedmindset.components.*"> <components:ComponentMXML label1="1" label2="2" label3="3" title="MXML Component" status="working"> <mx:DataGridColumn headerText="hello"/> <mx:DataGridColumn headerText="world"/> </components:ComponentMXML> <components:ComponentAS label1="1" label2="2" label3="3" title="AS Component" status="working"> <mx:DataGridColumn headerText="hello"/> <mx:DataGridColumn headerText="world"/> </components:ComponentAS> </mx:Application> |
And to no surprise, the two components look at act exactly the same.

That is it, the Actionscript Component matches the MXML component and the transfer was pretty straightforward.
Make sure to check out the Connect Meeting, March 12th 09 at 8pm for this presentation.





[...] Next Post: Turning Components From MXML To Actionscript – Part 3 [...]
this is Superb.. This is how a useful basic topic present in a neat way. Really fantastic.
Thanks! Glad it helps.
Thanks for your valuable post
How you change cornerRadius of buttons from AS?
As the cornerRadius is a style you can set the style with the following code:
yourButton.setStyle(“cornerRadius”,20); < 20 being the cornerRadius you are setting.
I do usually recommend to set these properties in CSS rather than the layout though, just my preference.