Turning Components From MXML To Actionscript – Part 2

We ended the last segment with just creating a very simple mxml component that doesn’t do much at all. For beginners the last post may have been interesting but we are going to go from introduction to advanced pretty quickly in this mini-series.

The next steps that we need to take with this component will be to add some other properties, events, and binding to our component so that it actually DOES something.

First I am going to concern myself with the properties for my component. I am going to want to make it so that the labels for the buttons are customizable externally from the component and also that the columns are customizable for the datagrid from outside of the component.

First we will add the properties to a script block and mark the properties as public so they can be adjusted externally.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version="1.0" encoding="utf-8"?>
<mx:Panel xmlns:mx="http://www.adobe.com/2006/mxml"
    layout="vertical"
    width="100%" height="100%">
    <mx:Script>
        <![CDATA[
           
            public var label1:String = "Button";
            public var label2:String = "Button";
            public var label3:String = "Button";
           
            public var columns:Array;
        ]]>
    </mx:Script>
    <mx:DataGrid width="100%" height="100%"/>
    <mx:ControlBar>
        <mx:Button label="Button"/>
        <mx:Spacer width="100%"/>
        <mx:Button label="Button"/>
        <mx:Spacer width="100%"/>
        <mx:Button label="Button"/>
    </mx:ControlBar>
</mx:Panel>

And next we bind the properties to the components so that the properties update the components if their values change. For the next part we will add the [Bindable] metadata tag and the use the {} to signify that the properties are bound to the variable.

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
<?xml version="1.0" encoding="utf-8"?>
<mx:Panel xmlns:mx="http://www.adobe.com/2006/mxml"
    layout="vertical"
    width="100%" height="100%">
    <mx:Script>
        <![CDATA[
           
            [Bindable]
            public var label1:String = "Button";
            [Bindable]
            public var label2:String = "Button";
            [Bindable]
            public var label3:String = "Button";
            [Bindable]
            public var columns:Array;
        ]]>
    </mx:Script>
    <mx:DataGrid width="100%" height="100%" columns="{this.columns}"/>
    <mx:ControlBar>
        <mx:Button label="{this.label1}"/>
        <mx:Spacer width="100%"/>
        <mx:Button label="{this.label2}"/>
        <mx:Spacer width="100%"/>
        <mx:Button label="{this.label3}"/>
    </mx:ControlBar>
</mx:Panel>

The next step we need to take is add some custom events because we will want to know if the user clicks on button 1 vs button 2 vs button 3. Yes, you could always watch for the click event and then see where the event originated from, but I feel that the event should be descriptive to the gesture. So we will add three custom events: button1Click, button2Click, button3Click.

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
<?xml version="1.0" encoding="utf-8"?>
<mx:Panel xmlns:mx="http://www.adobe.com/2006/mxml"
    layout="vertical"
    width="100%" height="100%">
    <mx:Metadata>
        [Event(type="flash.events.Event",name="button1Click")]
        [Event(type="flash.events.Event",name="button2Click")]
        [Event(type="flash.events.Event",name="button3Click")]
    </mx:Metadata>
    <mx:Script>
        <![CDATA[
           
            [Bindable]
            public var label1:String = "Button";
            [Bindable]
            public var label2:String = "Button";
            [Bindable]
            public var label3:String = "Button";
            [Bindable]
            public var columns:Array;
           
            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"));
            }
        ]]>
    </mx:Script>
    <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>
</mx:Panel>

Admittedly, there are many other events we could add to the component, but we will start with just these 3. In the last segment we added some private handler functions to dispatch the button click event and also added the event meta data so that the compiler knows what additional events are being thrown by this component.

Now we can add additional custom components to our main application with ease.

1
2
3
<components:ComponentMXML label1="1" label2="2" label3="3"/>
<components:ComponentMXML label1="a" label2="b" label3="c"/>
<components:ComponentMXML label1="Hello" label2="World" label3="UM"/>

Lastly, I will want to set a default property so that the compiler understands what default property to add content to if a user adds additional MXML in the component without specifying a specific property, as such:

1
2
3
<components:ComponentMXML label1="1" label2="2" label3="3">
<!-- do some mxml here -->
</components:ComponentMXML>

That small change looks like:

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
<?xml version="1.0" encoding="utf-8"?>
<mx:Panel xmlns:mx="http://www.adobe.com/2006/mxml"
    layout="vertical"
    width="100%" height="100%">
    <mx:Metadata>
        [DefaultProperty("columns")]
        [Event(type="flash.events.Event",name="button1Click")]
        [Event(type="flash.events.Event",name="button2Click")]
        [Event(type="flash.events.Event",name="button3Click")]
    </mx:Metadata>
    <mx:Script>
        <![CDATA[
           
            [Bindable]
            public var label1:String = "Button";
            [Bindable]
            public var label2:String = "Button";
            [Bindable]
            public var label3:String = "Button";
            [Bindable]
            public var columns:Array;
           
            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"));
            }
        ]]>
    </mx:Script>
    <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>
</mx:Panel>

So now we can add in columns to our component in the mxml, our new application looks as follows:

1
2
3
4
5
6
7
<?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>
</mx:Application>

customcomponent5

We now have a very simple and helpful component for our applications that does everything that we needed in MXML. The next step will be to go to the advanced level and turn this MXML component into Actionscript.

Next Post: Turning Components From MXML To Actionscript – Part 3

Make sure to check out the Connect Meeting, March 12th 09 at 8pm for this presentation.

  • Share/Bookmark

Comments (10)

[...] Next Post: Turning Components From MXML To Actionscript – Part 2 [...]

DKJuly 30th, 2009 at 7:52 am

I made a a custom component in MXML and I bring it into my application using Actionscript, how can I add my custom event to the main application so I can use a handler? I have the metadata tag in the component, but I can’t set the custom event on the main app? What am I missing?

Jonathan CamposJuly 30th, 2009 at 8:54 am

I am suspecting from your question that your component is what actually fires your custom event. With that assumption you are wanting your main application to listen for this custom event. My question is: Is this component directly off of the main app? Or nested down in the view hierarchy?

I ask because if it is nested you will probably have to make sure the event bubbles if you are wanting to listen for it at the main app level. If it is not nested then the procedure is pretty simple. I am going to make many assumptions about your app right now, but let’s just say that your component is of type “MyComponent”, with a custom event typed “CustomEvent”, and the event type “MY_EVENT”. And let’s say on the creationComplete handler you want to add your “MyComponent” and set up your listener. In the script block, that would look like….

1
2
3
4
5
6
7
8
9
private function _creationComplete_Handler(event:FlexEvent):void{
     var c:MyComponent = new MyComponent();
     c.addEventListener(CustomEvent.MY_EVENT,_customEvent_Handler);
     addChild(c);
}

private function _customEvent_Handler(event:CustomEvent):void{
     Alert.show("custom event fired");
}

Obviously this is at it’s simplest, but hopefully you see what I am thinking. Is this where you were going with it or did I miss something?

DKJuly 30th, 2009 at 11:26 am

Wow, thanks for the response, but I am thrown off track a bit. I will try to break it down and clarify. I really appreciate your input as I am on a deadline. You are awesome!

I made a component called CanvasServices.mxml. In this component I have this click handler that dispatches the event called “OtherContentClick”

public function LinkClicked(event:MouseEvent):void{

var linkinfo:String = event.target.id.toString();

switch (linkinfo)
{
case “maskedImage”:
LinkInformation = “Contact”;
break;

case “maskedImageLower”:
LinkInformation = “Form”;
break;

}

var eventObj:Event = new Event(“OtherContentClick”);
dispatchEvent(eventObj);

}

In the component I have this metadata tag

[Event(name="OtherContentClick", type="flash.events.Event")]

Now normally I would just call out the mxml component in tag form and if I typed in OtherCon- the event would show up in the code help and I would just handle as an attribute of the tag, but if I call it out and Addchild to another canvas in the script, I can’t get to the property of the event.

var ServicesContent:CanvasServices = new CanvasServices();
ServicesContent.y = 20;
ServicesContent.x = 10;
CanvasOtherContent.addChild(ServicesContent);
//I want to addeventlistener to the CanvasOtherContent, but can’t access the event

So, what does adding the mxml () do in the background that allows me to get to the event that I can’t do in the script?

Thanks again!
DK

Jonathan CamposJuly 30th, 2009 at 12:51 pm

Okay, I see what the real problem is. The thing to know is that the intellisense (the code completion helper) doesn’t “know all”. Some times there is a class it can’t find or an event it doesn’t know. Part of this problem is the intellisense, part of it is how you set up your component to work with the intellisense.

The short answer is: Add in your event listener with:

1
ServicesContent.addEventListener("OtherContentClick",_yourHandler);

I bet you everything will work just as you expect and you won’t receive any errors.

That is the short answer, the long answer doesn’t take a ton of time, but it does take being very exact with your component setup.

1. Usually all variables are camel case. ServicesContent, should be servicesContent.
2. Event names are also camel case. OtherContentClick should be otherContentClick.
3. The intellisense is trying to find your event, but what it is doing is looking at the type and finding Event. Then it is trying to show the OTHER_CONTENT_CLICK constant from the event. It isn’t finding this information, so it just doesn’t show anything. What you would have to do to get everything to show the way you want is to create your own custom event (extended from the Event base class) and include a constant like so…

1
public static const OTHER_CONTENT_CLICK:String = "otherContentClick";

Don’t forget to update your metatag!

1
[Event(name="otherContentClick", type="com.your.packaged.events.CustomEvent")]

Then in your event you would dispatch.

1
dispatchEvent(new CustomEvent(CustomEvent.OTHER_CONTENT_CLICK));

and THEN (long way, I know) your intellisense will work in your actionscript for:

1
servicesContent.addEventListener(CustomEvent.OTHER_CONTENT_CLICK, _handler);

With the CustomEvent automatically in your drop down list.

Remember, short answer is: It’s there, the intellisense just doesn’t know it. And all that event listeners are really looking for in the event stream is a string, so just make sure the strings match. That is why people use constants, makes it easier to reduce string mistype.

DKJuly 30th, 2009 at 5:47 pm

Thank you so much for the clarification! A lot of those terms I knew and used once or twice, but it put some light on it. I still cannot get my function that just shows an alert that says “please work!” to fire. I did the

ServicesContent.addEventListener(“OtherContentClick”,ChangeStateServices);

and that didn’t work but the mxml version does work.:

The reason I don’t just do the example above is that I have about 5 of those components that I am going to addchild and remove child to all of them. Also, the htmlText doesn’t render the same in the mxml tag version, but renders correctly in the addChild version.
So, what could be the deal? I have tried the extended event and couldn’t quite get the syntax
here it is:
package events
{
import flash.events.Event;

public class OtherContentClicked extends Event
{
public var canServ:CanvasServices;

public function OtherContentClicked(type:String, bubbles:Boolean=false, cancelable:Boolean=false)
{
super(type, bubbles, cancelable);
this.canServ;
}

public override function clone():Event
{
retutn new OtherContentClicked();

}

}
}

DKJuly 31st, 2009 at 7:27 am

Hello,

The above post removed my mxml tag that I had originally posted. That was the “example above” that never showed up in the comment. I apologize if that was confusing. So, I removed the start and end tags

local:CanvasServices x=”10″ y=”20″ OtherContentClick=”ChangeStateServices()”

The event shows up and I can use the event using this method, but like I said above the htmlText property shows up without line breaks and without the bulleted lists, but using the addChild method it renders perfectly. So, I would like to set the event listener in the code.

Thanks!

Jonathan CamposJuly 31st, 2009 at 9:00 am

If you don’t mind, email your code directly and I will make my changes and test, I am sure we can get this working. jonbcampos at gmail

I am noticing that your implementation of the custom event is missing a constant and also missing variables passed in the clone function. You need to pass on the variables from the currently constructed event to the cloned event so those variables are available to the new event.

DKJuly 31st, 2009 at 5:32 pm

I got it! Your steps helped me to figure it out. I created the custom event, but I didn’t use the clone, but I did use the super. Thanks so much! I’ll send you the custom event code so you can see if I may need the clone! Thanks so much!

Jonathan CamposJuly 31st, 2009 at 7:34 pm

Here is your event code:

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
package events
{
    import flash.events.Event;

    public class OtherContentClicked extends Event
    {
        //public var canServ:CanvasServices;
        public static const OTHER_CONTENT_CLICK:String = "otherContentClick";
        public var link:String;
       
        public function OtherContentClicked(type:String,link:String=null,bubbles:Boolean=true, cancelable:Boolean=false)
        {
            this.link = link;
            super(type, bubbles, cancelable);
           
        }
       
        /*public override function clone():Event
        {
            retutn new OtherContentClicked();
           
           
        }*/

       
    }
}

And here another version with just a few changes I would make that I think you would agree help things work a bit better. I included note through the code.

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
package events
{
    import flash.events.Event;

    public class OtherContentClicked extends Event
    {
        //public var canServ:CanvasServices;
        public static const OTHER_CONTENT_CLICK:String = "otherContentClick";
       
        private var _link:String;
        //we make it so the link can't be changed as the event moves through the event flow.
        public function get link():String{
             return _link;
        }
       
        public function OtherContentClicked(type:String,link:String=null,bubbles:Boolean=true, cancelable:Boolean=false)
        {
            _link = link;
            super(type, bubbles, cancelable);
           
        }
       
        public override function clone():Event
        {
            // you need to include the vars from the current event to the new clone
            return new OtherContentClicked(type, link, bubbles, cancelable);
        }
       
    }
}

Hope that helps!

Leave a comment

Your comment