Give us a call 203-379-0773
Check out our new training course on AngularJS for Flex Developers

How does one Flex Component talk to another Flex Component?

I think there is one question that has come up hundreds of times on Stack Overflow. The question is "How does one Flex Component call a method in another Flex Component?" or some variation thereof. Sometimes people want to access something in the main application, sometimes they want to access something inside an itemRenderer, other times they want to access another child of a ViewStack. No matter the purpose, I feel it is time I create the most detailed answer to the question I can.

The Setup

First let's pretend you have a setup like this:

Click the image to see a larger version.

Something like this would not be uncommon in an application you were building with Flex. At the root of everything in a Flex Application is your main Application file, usually extending s:Application or mx:Application.

Inside the application contains two main components, ComponentA and ComponentB. These could represent two separate pieces of the app and are never showed together at the same time. Or they could represent two different components that are both shown at the same time and make up a single screen.

Inside Component A, is a ViewStack with three children. ComponentB contains a list class, which could be a List or a DataGrid or something else. The List class uses an itemRenderer. Many beginner developers make the mistake of thinking that the itemRenderer represents a single component; however a new instance of the itemRenderer is created for every item displayed in the list. It is an important distinction that many people do not understand.

The implementation of an application like this could be done across many different files or it could be done in a single MXML file. When creating a single MXML file, the parent child relationship can be hidden from the beginner, because it looks like the main application is the parent of every child. It is important to remember the true component hierarchy when thinking about how components should interact with each other.

Events Communicate Up

Many questions relate to executing a method in their parent from a child. For example, what if you want to change the selectedIndex of the ViewStack from within Child1? Perhaps you're building a multi-step process and the user just clicked a 'next' button. Or perhaps the user was viewing data in a DataGrid and clicked an 'edit' or 'more details' button that is supposed to open up a new screen with additional information. All these are valid use cases. So, how do you do it?

From an encapsulation stand point, a component should never try to execute methods in its parent. Doing so gives the component a dependency upon the parent; thus minimizing reuse. The child component cannot then be used as the child of a component other than the parent

The solution for a component to tell its parent to do something is to use events. An event is dispatched from the component to its parent. The parent will listen to events from its child and execute handler methods to perform some functionality. Flex makes use of this model all over the place such as the click on a button or the input of keystrokes into a TextInput.

The code isn't that difficult. First create an event class:

var myEvent = new Event('myEventType');

And then dispatch the event:

dispatchEvent(myEvent);

If you need to send custom data with your event; you can create your own event class to do so; but I decided to keep such details out of this article. For bonus points you can use metadata to define your event as part of the class. This is primarily used for code hinting within an IDE and does not affect things at runtime. This is sample event metadata:

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

You can put that metdata before the class definition if you have an ActionScript class; or as part of the fx:Metadata tag in an MXML class.

Dispatching the event is only the first step, though. The parent is going to have to add an event listener to it. In MXML this is easy, as the event will show up as if it were a property on the component, like this:

<ns1:Child1 myEventType="myEventHandler(event)" />

If you want the above method to work, you'll need to add in the event metadata. However, if for some reason you don't want to create event metadata, you can also add listeners in ActionScript. You use the addEventLIstener method on the instance of your component. The instance is named by using the id tag in MXML or the variable name in ActionScript:

myChild1.addEventListener('myEventType',myEventHandler);

Either method for adding an event listener will call a method, known as the event handler:

protected function myEventHandler(myEventHandler:Event):void{
// do stuff here
}

Events are the proper way that a component can communicate with its parent. If the component is on the display list; the events can also bubble. Using our example, if myChild1 dispatches the myEventType; first the ViewStack event handlers will execute. If ComponentA added a listener on the ViewStack for the myEventType event; that will execute next. If the main application had an event listener on ComponentA for the myEventType, that event handler will execute next.

Event bubbling is the proper way for a renderer to communicate with the parent of its List. Dispatch an event from the renderer, make sure it bubbles by setting the bubble property of the event's constructor to true, and then listen for the event on the List.

Inside the renderer, you create the event, with the bubble flag to true and dispatch it normally:

var myEvent = new Event('myEventType', true);
dispatchEvent(myEvent);

In ComponentB you can add an event listener to your List class:

List.addEventListener('myEventType', myEventHandler);

You won't be able to add the event listener in MXML unless you extend the list class to include custom metadata as we showed above. But, even without metadata the event will still bubble and the event listeners will still execute.

Methods Communicate Down

Events are how a component should communicate up to its parents. But, how does a parent communicate down to its children? There are two different ways, either by executing a public method on the child or by changing properties on the child. Public methods and properties make up the API which a component expects its parents to use to control its behavior.

Creating a public method is no different than creating an event listener, like we did above. It just uses a different access modifier, specifying public instead of protected:

public function myMethod(argument:ArgumentType):void{
// do stuff here
}

You can specify as many arguments as are needed to perform the method functionality. A good example of this is the addEventListener method. That is a method built into Flash's architecture, but you can write your own methods using the approach above to perform your own functionality. The Flextras Calendar component, for example, has a method to change the view between month, week, and year.

A component would call this method on the instance of the component, specified by the MXML ID. So, if the ViewStack component wanted to call myMethod on the Child2 it would do this:

myChild2Instance.myMethod(myArgument);

Methods can return things by specifying the return type in the method signature. Our example above specifies the return type as void, which means the method doesn't return anything. Here is another method which returns a Boolean value:

public function myMethod(argument:ArgumentType):Boolean{
// do stuff here
// return result
return true;
}

The code you put inside the method, the arguments you choose, and the return value will all depend upon the functionality you want to achieve in the method.

Properties Communicate Down

Changing properties values are the second way that a parent can communicate with its child. As one example, a component's position in the display list is defined with the properties X and Y. The labelField on a list, or the selectedIndex property on a ViewStack are other examples of properties. Properties are implemented as public variables within a component.

They can be implemented with a simple variable definition:

public var myProperty :MyType;

This is the easiest way to create a property on a component. You can also create properties using a special set of methods, named get and set methods. If you want to perform other functionality when setting, or retrieving the value of your variable, then getter and setters are what you need. This is a common implementation:

private var _myProperty :MyType
public function getmyProperty():MyType){
return _myProperty;
}

public function set myProperty(value:MyType):void{
_myProperty = value;
}

The simple public variable becomes private; and the API for setting or retrieving these methods lie within the get and set methods which access the private value. It is very common in the Flex Framework for a set method to be used to dispatch a change event for the component. If you want a property to be read only, you can leave out the set method.

Whether implemented as simple variables, or with get/set methods, properties are accessed identically from the component trying to change the property. For example if ComponentB wants to set the selectedIndex on the List it would do this:

list.selectedIndex = 1;

It doesn't matter how the list has implemented the property for selectedIndex. This is a form of encapsulation I call implementation hiding. One component does not need to know how another is implemented. It should only communicate with it through its public API. The API is defined through documented properties, methods, and events.

Other Thoughts

Components that are on the same level of each other, such as Child1 and Child 2 should not communicate directly with each other. The proper way to have Child1 affect Child2 is to dispatch an event from Child1; have your ViewStack component listen to the event and call a method, or property, on Child 2.

There are other methods for sharing data between components, such as using Dependency Injection to share class instances between components. Another may be to use a Singleton class. Many frameworks exist to help with these things, but it is beyond the scope of this article. Your key takeaway from this article is that the use of events, properties, and methods can help you create robust, reusable components within your application.

Why isn't my hostComponent property getting set in my Mobile Skin?

When Building Spark skins , you have two classes at play. One is a component class that extends SkinnableComponent. The other is the skin class. When building mobile components, your skin class should extend MobileSkin.

As part of extending MobileSkin, the Adobe documentation recommends that you implement a hostComponent property on the skin class. During component creation; the SkinnableComponent will call createChildren(), which will call attachSkin(). attachSkin() will try to set the hostComponent property on the skin class to an instance of the component class.

Adobe has provided some error trapping when this happens. Here is the code:

if ("hostComponent" in skin)
{
try
{
Object(skin).hostComponent = this;
}
catch (err:Error) {}
}

The set method will fail silently if setting the hostComponent throws an error. This is good in production because you don't want your users to see errors. But, it is bad during development, because any error in the set hostComponent set method will, effectively, be invisible to you as the developer.

I had an error in my hostComponent set method. When I Tried to access the hostComponent property later in the component, it was null and I had no idea why it wasn't getting set.

In the Flextras AutoCompleteComboBox Mobile Skin I'm using the set hostComponent method to add an event listener to the hostComponent. If the hostComponent changes, it removes the old event listener before changing the value. Unfortunately, on the first 'set' it threw an error because there was no component instance to remove the event listener from. This was the code:

public function set hostComponent(value:ComboBox):void
{
_hostComponent.removeEventListener('someEvent',onSomeEvent);
_hostComponent = value;
_hostComponent.addEventListener('someEvent',onSomeEvent);
}

When I wrote it, it seemed logical, and I just didn't catch the error. In normal circumstances the run-time error would hvae alerted me immediately. But, in this case the SkinnableComponent trapped the error and I was left debugging a completely separate "null access" error at some later point in the code.

This is how I fixed it:

public function set hostComponent(value:ComboBox):void
{
if(_hostComponent){
_hostComponent.removeEventListener('someEvent',onSomeEvent);
}
_hostComponent = value;
_hostComponent.addEventListener('someEvent',onSomeEvent);
}

There aren't a lot of places in the SDK I have caught Adobe providing their own error checking like this, so it was an unexpected to me.

Why doesn't my Mobile Skin show focus properly?

I'm working hard on a mobile version of the Flextras AutoCompleteComboBox. I spent some time building out an ActionScript only skin that extended the MobileSkin class.

Unfortunately, I was having a weird issue. The focus ring was not showing up properly. I was seeing this:

You'll notice that the blue focus ring shows up on top of the component, but does not show up below it.

I wanted to see this:

Which shows the focus ring correctly highlighting the whole component. The above screenshot was taken with an 'early' MXML version of the mobile skin.

Why would this work for an MXML skin, but not for an ActionScript skin that was almost identical?

It turns out, the issue was I did not implement a measure method; so no measuredHeight or measuredWidth were being set. Once I implemented that method; my focus issues went away.

Building Mobile Skins in Flex 4.5

Flex 4.5 was recently released, and one of the big pushes for that is mobile support. When building mobile components, we want to build ActionScript Skins for them instead of MXML skins, as we did with Flex 4 and Flex 4.1. This is a bit of a change for us, as Adobe always pushed us to use MXML when creating skin classes.

I believe that the push to use ActionScript over MXML is due to performance. MXML is really just an ActionSript code generation language; and sometimes it does some unexpected things under the hood. Writing skins in ActionScript is one way to avoid the potential wonkyness that may occurs when MXML is turned into ActionScript.

MobileSkin Class

To help build skins, there is a new MobileSkin class as part of the SDK. This is a class that extends UIComponent and provides a bunch of methods to help us build mobile skins. As best I can tell, there is no penalty for not extending the Mobile Skin class. You could extend UIComponent or some other Flex component to create your skin. But, unless you have a compelling reason to break convention, I'd stick with what adobe recommends.

Extra MobileSkin Methods

MobileSkin provides some special methods that you can override or extend. The extra methods are not much different, conceptually, than the Spark methods such as partAdded() and partRemoved(). These are some of the extra methods:

  • layoutContents(): The layoutContents() method is used to size and position the children of your component. It is called from the updateDisplayList() method.
  • drawBackground(): The drawBackground() method is used to create your background graphics. It is called from updateDisplayList(). In the mobile skin for our upcoming mobile AutoCompleteComboBox we use graphics.drawRect() to create a background on the popup. This is a replacement for the MXML Graphics used in the default ComboBox skin.
  • commitCurrentSkinState(): The commitCurrentSkinState() method is used to help implement states. Whenever the state changes, the commitCurrentSkinState() method is called. States in a Mobile Skin should not be implemented as real states that extend the state class, with multiple overrides to change things. Instead they should be implemented procedurally within the skinClass. You should tie into the component lifecycle methods to make the relevant changes to properties, styles, positioning, and layout. This method is called from the currentState set method.
  • hasState(): The hasState() method is a helper method that you should implement. It can tell people using your skin class whether this skin class implements a certain state, or not. As best I can tell this is primarily used for people extending your mobile class. I envision it being important to the extensibility of the class; but you may let it slide by the wayside if you're building a single use component.

There are also a few methods which I consider helper methods. They aren't implemented for you to extend, but are rather implemented to help you solve a specific task quickly:

  • setElementSize(): The setElementSize() method is a helper for setting the size of a child. This handles the differences between child types, such as ILayoutElement or IFlexDisplayObject. The documentation recommends you always set an elements size before setting its' position.
  • setElementPosition():The setElementPosition() is a helper method for setting the x and y values of a child. This handles the differences between child types, such as ILayoutElement or IFlexDisplayObject.. Documentation states you should always set the elements size before setting its' position.
  • getElementPreferredHeight(): This method is used to get the preferred height of the child. If the child is an ILayoutElement, then the getPreferredBoundsWidth() value will be returned. If it is an IFlexDisplayObject, then the measuredWidth will be returned. If the object is neither of those, then the height of the object is returned.
  • getElementPreferredWidth(): This method is used to get the preferred height of the child. If the child is an ILayoutElement, then the getPreferredBoundsHeight() value will be returned. If it is an IFlexDisplayObject, then the measuredHeight will be returned. If the object is neither of those, then the width of the object is returned.

These methods help encapsulate setting the size and position of your child, no matter what the type of the child is.

MobileSkin extends UIComponent

I mentioned earlier that the MobileSkin class extends the UIComponent class. This is important because it means that to implement our layouts; we can tie into the same component lifecycle methods that we're used to using from learning the Halo Architecture:

  • createChildren(): The createChildren() method is the method you use to create your component's children. It is called automatically during the lifecycle creation.
  • commitProperties(): commitProperties() is used to coordinate property changes in a component. It is a bit of a wild card method, and what you do with this will relate specifically to what properties are changing, and how they change.
  • measure(): The measure() method is used to determine the ideal height and width of a component. It sets the measuredWidth and measuredHeight properties. OF course, a component is always sized by its' parent, and it is up to the parent to use these values or not. I believe al Flex Framework containers will honor these values if possible.
  • updateDisplayList(): The updateDisplayList() method is used to position and size the children of a component. When extending a MobileSkin you'll probably use layoutContents() and drawBackground() instead of updateDisplayList() directly.

The HostComponent Property

In Flex 4 when we created a skin, we would specify the component class that this skin accessed using the hostComponent metadata. It looked something like this:

<fx:Metadata>
[HostComponent("mobile.flextras.autoCompleteComboBox.AutoCompleteComboBox")]
</fx:Metadata>

We do not use metadata to define the host component in an ActionScript skin. Instead we create a property named hostComponent. The properties type should be of the component class that the skin was designed for. I would use something like this:

public var hostComponent: AutoCompleteComboBox;

The hostComponent property is set automatically during lifecycle creation. It gives the skin class a hook into the component class. As one example of a use case, our AutoCompleteComboBox mobile skin uses the hostComponent property to reference the number of items in the dataProvider, and uses that to determine the size of the drop down.

Final Thoughts

Building Mobile Components feels like a nice intersection where the Spark and Halo component architectures meet. You'll need to know both lifecycles in order to be successful at creating components for use in Mobile development.

Why can't I access mobile skins in my Flex Library Project?

I'm working on a version of the Flextras AutoCompletComboBox for mobile devices. Many if you may have seen me demoing an early version of that at the 360|Flex conference. With the formal release of Flex 4.5 on the horizon, I came across a problem.

When trying to create a Flash Builder Library Project that supported mobile components, I could not access mobile skins. This seemed odd to me; as I could access them in a Flash Builder Mobile project. As best I could tell the list of SDK SWCs was identical. After some digging, I think I found the issue and a possible solution.

The Issue

One of the things I've done to prep the AutoComplete component for mobile usage is to create a custom, mobile optimized skin. Our AutoComplete is a composite of Skin Parts which do have mobile support, such as the Button, TextInput, and List. I wanted to apply the mobile skins for those components to the default AutoCompleteComboBox skin. Unfortunately, this gave compiler errors for trying to access undefined classes.

I believe the issue relates to where the mobile component skins are stored. They are compiled into a specific theme class; which is not automatically applied to mobile library projects. I suspect in many use cases, you wouldn't want to apply a theme to a Flex Library that may be overwritten when a real project is compiled. However, with the new Spark Architecture; where skins are classes [not necessarily just CSS and assets] it seems that referencing skin classes inside a library project seems more likely.

The Solution

The fix I found was to add add the mobile theme SWC to the library path. Details are here:

  1. Bring up project properties -> Flex Library Build Path -> Library Path
  2. Click "Add SWC" button
  3. Enter "C:\Program Files (x86)\Adobe\Adobe Flash Builder 4.5\sdks\4.5.0\frameworks\themes\Mobile\mobile.swc" and click okay.
  4. Click Okay on the dialog window
  5. click okay in project properties

I still get compiler warnings, which is undesirable, but it's better the compiler errors that do not allow me to create a SWC at all.

The full details of my work around are in this bug post. Please vote for it; as that will raise its awareness in Adobe. I believe the more people who vote, the more likely it is to be fixed.

Flex Mobile Skins - 4/29/2011 - Episode 100 - Flextras Friday Lunch

Today I talk about building Flex Mobile Skins.

Flextras Friday Lunch is a short demo followed by open Q&A that occur the first and third Friday of every month at 1pm EST. The presentations take place over Connect. You can find out what time this occurs in your own time zone at TimeAndDate.com.

I hope I can help answer some of your questions at the next one.

Notes

Contact

getCurrentSkinState() - 12/10/2010 - Episode 89 - Flextras Friday Lunch

Today, I spoke about the getCurrentSkinState() method of the Flex Spark Component LifeCycle. Conversation turns to the Playbook, and version control.

Flextras Friday Lunch is a short demo followed by open Q&A that occur every Friday at 1pm EST. The presentations take place over Connect. You can find out what time this occurs in your own time zone at TimeAndDate.com.

I hope I can help answer some of your questions at the next one.

Notes

Contact

partAdded() and partRemoved() - 12/03/2010 - Episode 88 - Flextras Friday Lunch

This time around, I talk about partAdded() and partRemoved() two pieces of the Spark Component LifeCycle. Conversation turns to mobile and tablet development, the playbook and the nook, and mind mapping.

Flextras Friday Lunch is a short demo followed by open Q&A that occur every Friday at 1pm EST. The presentations take place over Connect. You can find out what time this occurs in your own time zone at TimeAndDate.com.

I hope I can help answer some of your questions at the next one.

Notes

Contact

Creating Custom Flex Layouts - 8/6/2010 - Episode 73 - Flextras Friday Lunch

This time we talk about creating custom Flex layouts and I talk about the Flex 4 version of The Flex Show's CalendarDisplay class, available only on the premium USB Drives. We talk a bit about my equipment setup an Flash Player 10.1 on mobile devices.

Flextras Friday Lunch is a short demo followed by open Q&A that occur every Friday at 1pm EST. The presentations take place over Connect. You can find out what time this occurs in your own time zone at TimeAndDate.com.

I hope I can help answer some of your questions at the next one.

Notes

Contact

Why won't my ASdocs Copy?

This post comes from the monthly Flextras newsletter. While we were finishing up the Flextras Calendar, we wrote over 27 pages of ASDocs. I was presented with an interesting issue with ASDocs.

What are ASDocs?

ASDocs, if you're unaware, are special blocks of code you write in your Flex files that can be run through a command line utility to generate an API Reference. The API References you use for the Flex Framework are generated by the ASDocs tool, for example.

ASdocs look like this one, from the changeMonth method of the Flextras Calendar:

/**
* A method to change the month based on the specified increment.
* You can use a negative value to go back in time or a positive value
* to go forward.
* Only has an effect if in the MONTH_VIEW state.
*
* @param value The increment value used to change the month; the
* default is 1.
*
* @see #MONTH_VIEW
*/

The ASDoc are placed before elements in your files, such as properties or method and when you run the ASDoc tool it picks up the text and adds it to the generated ASDocs files.

In the ASDocs segment above you see two ASDoc tags, @param and @see. These tags give special instructions to ASDocs. The @see tags add links to other elements of the API, other classes, or even URL links. The @param tag defines the parameter to a method, and they are given a special place in the ASDoc output.

The @copy tag?

Our Calendar supports most elements of the List class API and the DateChooser API. The Calendar extends ListBase, so most of the properties are inherited. The DateChooser properties, such as firstDayOfWeek, monthNames, or dayNames are implemented as if they were brand new.

We thought instead of writing new documentation for these properties, we could use another ASDoc tag--@copy--to just copy the documentation from the DateChooser class into the Calendar class. The code looked like this:

/**
* @copy mx.controls.DateChooser#firstDayOfWeek
*/

This seemed logical to us, but for some reason the ASdocs wouldn't copy; and the documentation in our generated file was blank for those copied properties. What was the problem?

It turns out that the DateChooser is never used anywhere in our Calendar. As such, the Flex Compiler will automatically optimize the class right out of the SWC file. This is good because it keeps file size low and you do not have to distribute unnecessary classes. Unfortunately, it seems to the ASDoc appears to use the same approach. DateChooser wasn't in the API, so therefore the ASDoc utility couldn't find the DateChooser code in order to copy the appropriate ASDocs from one place to another.

The Solution Part 1!

One solution, and I hated doing this, was to create a dummy variable of the DateChooser type:

private var temp : DateChooser;

Then all was well and the properties would get documented as expected. I don't like this because it artificially inflates the size of the SWC. Thankfully there is a better solution.

The Solution Part 2!

The ASDoc command line tool supports many of the same command line arguments that the Flex compiler supports. We can use the include-libraries argument and point it at the framework SWC. This will make all classes in the framework available to the ASDoc compiler whether or not they are used.

For practical purposes, you want limit your use of this argument when creating swfs for wide release. But, none of that matters when generating ASDocs.

Conclusion!

Did I mention that the Flextras Calendar is out? It supports Flex 3 and Flex 4 and you can download a free developer edition to test it out.

More Entries