How to get the real location of a point in Flex

When you create a point within a component the Flash player uses the component’s coordinate space. So for example, if your custom component is 200 pixels from the top of the screen and you create a point within it. If you set the point to a Y value of 0 it will be at the top of the component (which means it will be 200 pixels from the top).

Originally I had written a helper class which would recurse its way up the display chain and total up all the values of the X’s and Y’s, but there’s a far simpler way to get the point’s real location.

You can use the localToGlobal function to translate a point’s coordinate space. For more information check out the Adobe docs

Best,
Hillel

Flex Tip: Remove/delete item from an array

Updated March 10th, 2009
It turns out this is probably not the best way to do it. As per the comments below, be careful using the delete command as it will only set the element to undefined (the length of the array won’t be shortened. I considered deleting this post, but I think it may still be useful in that it helps clarify the difference between slice and delete.

Original post starts here…

If you search for this topic you’ll find that this can be accomplished using the splice function.

MyArray.splice(3, 1);

This was how I was doing it for a while, but I just recently discovered a simpler method.

delete MyArray[3]

Hope this helps,
Hillel

Flex Tip: Error accessing the stage when the app first loads

Long story short use the applicationComplete event.

This is something I struggled with for a while. I don’t use a mouse much and like to code keyboard shortcuts into the applications I build. My code initially looked like this:



Flex Tip: Check if string starts with…

I wrote a post a while back discussing how to optimize searching in Flex. I’ve noticed that people keep finding the post by searching for ‘flex string starts with’. The problem is the post doesn’t actually answer that question.

So… here’s how to do it:

public static function startsWith( string:String, pattern:String):Boolean
{
	string  = string.toLowerCase();
	pattern = pattern.toLowerCase();
			
	return pattern == string.substr( 0, pattern.length );
}

Since we’re talking about string searching I may as well throw out one more quick tip. The above code will match ‘ma’ in ‘Mary’ but let’s say the name is ‘Mary Anne’. Here’s how to match ‘ma’ or ‘an’.

public static function isMatch( string:String, pattern:String ):Boolean
{
	if (startsWith( string, pattern ))
	{
		return true;
	}
			
	var words:Array = string.split( " " );
			
	for each (var word:String in words)
	{
		if (startsWith( word, pattern ))
		{
			return true;
		}
	}
			
	return false;
}

One quick note, in the above code we search the full string first before searching each of the words. We’re doing this to make sure (using the last example of ‘Marry Ane’) that the function also matches ‘mary a’.

Best,
Hillel

Flex Tip: Convert XML to ArrayCollection

This is one of those things that took me much longer than I thought it would have. I needed to convert a file containing XML data to an ArrayCollection. My XML file looked like this:

<rows>
	<row>
		<col1>some value</col1>
		<col2>another value</col2>
		...
	</row>
	...
</rows>

I wanted to convert this into an ArrayCollection with each row as an object (and I didn’t want to have to loop through each item in the list). Here’s the solution I’m using.

import mx.utils.ArrayUtil;
import mx.rpc.xml.SimpleXMLDecoder;
import mx.collections.ArrayCollection;
			
private function convertXmlToArrayCollection( file:String ):ArrayCollection
{
	var xml:XMLDocument = new XMLDocument( file );
			
	var decoder:SimpleXMLDecoder = new SimpleXMLDecoder();
	var data:Object = decoder.decodeXML( xml );
	var array:Array = ArrayUtil.toArray( data.rows.row );

	return new ArrayCollection( array );
}

I’m not thrilled with this approach because I’ve needed to hard code the XML structure (’rows.row’) into the function. If anyone has a better solution I’d love to see it.

Best,
Hillel

Flex Error #1009: Cannot access a property or method of a null object reference

An update to this post can be found here

This was my first “bang my head against the wall for a really long time” Flex error. Here’s a simplified version of what I was doing wrong. I had a created a custom component which contained another Flex component.

<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml">
	<mx:Button id="button"/>
</mx:Canvas>

Then in my application I was using it as follows:

var myComp:MyComp = new MyComp();
myComp.button.label = "Button Label";
addChild( myComp );

The problem here has to do with what’s called the “component instantiation life cycle”. The basic issue is that when I was setting the label for the button it hadn’t yet been created. In the rest of the post I’ll cover three ways to fix this.

Solution 1 (don’t actually do this, just showing what’s possible)

The easiest way to fix this is to just add the component to the stage before setting the label.

var myComp:MyComp = new MyComp();
addChild( myComp );
myComp.button.label = "Button Label";

As you can see we’ve simply swapped lines 2 and 3. This is definitely not best practice. While it does work it means that you need to display your component before making any changes to it. This will be pretty inefficient as it will require the Flash player to update the component a second time.

Solution 2 (this one’s pretty good)

A better way to do this would be to add a variable to the custom component which stores the label. Then when the component is added to the stage it sets the button’s label to the value set. Here’s what it would look like

<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" initialize="init()">

	<mx:Script>
		<![CDATA[
			
			private var _buttonLabel:String;
			
			private function init():void
			{
				button.label = _buttonLabel;
			}
			
			public function set buttonLabel( value:String ):void
			{
				_buttonLabel = value;
			}
					
		]]>
	</mx:Script>
	
	
	<mx:Button id="button"/>
</mx:Canvas>

One thing worth pointing out is we’re using the initialize event rather than the creationComplete event. Setting the label for the button will cause the Flash player to recalculate the size of the button. If we used the creationComplete event it will have already measured the button. This means we’ll be forcing the player to measure it again.

To use this component we’d change the code slightly to use the new buttonLabel parameter:

var myComp:MyComp = new MyComp();
myComp.buttonLabel = "Button Label";
addChild( myComp );

Solution 3 (the real way to do it)

The catch with the 2nd solution is it only enables you to set the button label before you add the component to the stage. We could work around that by checking if the button already exists when setting the value for buttonLabel but we’re going to skip past that change and just implement it the real way.

<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml">

	<mx:Script>
		<![CDATA[
			
			private var _buttonLabel:String;
			private var _buttonLabelChanged:Boolean;
			
			public function set buttonLabel( value:String ):void
			{
				_buttonLabel = value;
				_buttonLabelChanged = true;
				
				invalidateProperties();
				invalidateSize();
			}
			
			override protected function commitProperties():void
			{
				super.commitProperties();
				
				if (_buttonLabelChanged)
				{
					button.label = _buttonLabel;
					_buttonLabelChanged = false;
				}
			}
					
		]]>
	</mx:Script>

	<mx:Button id="button"/>
</mx:Canvas>

By overriding the commitProperties function we enable the component to be far more efficient. The code which changes the button label will execute much quicker that the change will be displayed on screen. Using this approach even if we change the label many times, the Flash player will only recalculate the component once.

The purpose of the post was to show you how to resolve the “null object” error. While discussing this problem we’ve gotten in to the area of creating custom components but we’ve really just skimmed the surface. There are a ton of great tutorials online covering this topic in great depth. If you’d like to learn more a quick google search should find you everything you ever wanted to know (and probably more).

Best,
Hillel

Resource Bundles in Flex (w/o lots of extra code)

I’ve always thought of resource bundles as one of those things I would implement when I needed them (I’ve never needed to port an application I’ve built to another language). That said, there’s another great benefit to bundles which sometimes gets lost in the crowd. They allow non-developers to change the text displayed in the application. I can’t tell you how many hours\days\weeks I’ve spent making minuscule text changes (only to have them changed back later).

With this new appreciation of the power of resource bundles I’ve started to implement them in the latest application I’m working on. The problem I’ve run into is that they add a tremendous amount of bloat. In this post I’d like to show the technique I’m using to eliminate a lot of the extra code.

Here’s an example of the standard way to use resource bundles (this is a piece of component called MyForm.mxml)

<mx:Metadata>
	[ResourceBundle("MyForm")]
</mx:Metadata> 

<mx:Script>
	<!&#91;CDATA&#91;
		import mx.resources.ResourceManager;
	&#93;&#93;>
</mx:Script>

<mx:Form>
	<mx:FormItem label="{ resourceManager.getString( 'MyForm', 'firstNameTextInput' ) }">
		<mx:TextInput id="firstNameTextInput"/>
	</mx:FormItem>
	<mx:FormItem label="{ resourceManager.getString( 'MyForm', 'lastNameTextInput' ) }">
		<mx:TextInput id="lastNameTextInput"/>
	</mx:FormItem>
	...

</mx:Form>

The main thing that bothers me with the above code is that in most cases the parameters passed to the resourceManager shouldn’t need to be specified. The first parameter, the bundle name, is most likely going to be the same for all of the fields on the page. Additionally, the key for the field can (if you use consistent naming conventions) match the id of the input component.

The heart of this solution is a helper utility class called ResourceUtils.

Here’s our first pass at it.

package
{

import mx.resources.ResourceManager;

public class ResourceUtils
{
	private var _bundleName:String;
	
	public function ResourceUtils( bundleName:String )
	{
		_bundleName = bundleName;
	}
	
	public function getString( key:String ):String
	{
		return ResourceManager.getInstance().getString( _bundleName, key );
	}
}
}

To use this we’ll simply create an instance of it at the top of our component

<mx:Metadata>
	[ResourceBundle("MyForm")]
</mx:Metadata> 

<mx:Script>
	<!&#91;CDATA&#91;
		import ResourceUtils;

		&#91;Bindable&#93;
		private var _rb:ResourceUtils = new ResourceUtils( "MyForm" );
	&#93;&#93;>
</mx:Script>


<mx:Form>
	<mx:FormItem label="{ _rb.getString( 'firstNameTextInput' ) }">
		<mx:TextInput id="firstNameTextInput"/>
	</mx:FormItem>
	<mx:FormItem label="{ _rb.getString( 'lastNameTextInput' ) }">
		<mx:TextInput id="lastNameTextInput"/>
	</mx:FormItem>
	...

</mx:Form>

By using this class we’ve eliminated the need to specify the bundle name every time we want to look up a key. That’s pretty cool but I think we can do even better. The second part of this solution is going to enable us to auto-magically map the key values to the input components based on the component ids.

Here’s the final version of the ResourceUtils class

package
{

import mx.containers.FormItem;
import mx.core.UIComponent;
import mx.resources.ResourceManager;

public class ResourceUtils
{
	private var _bundleName:String;
	
	public function ResourceUtils( bundleName:String )
	{
		_bundleName = bundleName;
	}
	
	public function getString( key:String ):String
	{
		return ResourceManager.getInstance().getString( _bundleName, key );
	}
	
	public static function setLabels( container:UIComponent, bundleName:String = null ):void
	{
		if (!bundleName)
		{
			bundleName = container.className;
		}
		
		setChildLabels( container, bundleName );
	}
	
	private static function setChildLabels( container:UIComponent, bundleName:String ):void
	{ 
		for (var x:uint; x < container.numChildren; x++)
		{
			var child:Object = container.getChildAt( x );
			
			if (child is UIComponent)
			{
				ResourceUtils.setChildLabels( child as UIComponent, bundleName );
				
				var label:String = ResourceManager.getInstance().getString( bundleName, child.id );
				
				if (!label)
				{
					continue;
				}
				
				if (UIComponent( child ).parent is FormItem)
				{
					var formItem:FormItem = UIComponent( child ).parent as FormItem;
					formItem.label = label;
				}					
				else if (child.hasOwnProperty( "prompt" ))
				{
					child.prompt = label;
				}
				else if (child.hasOwnProperty( "label" ))
				{
					child.label = label;
				}
				else if (child.hasOwnProperty( "text" ))
				{
					child.text = label;
				}
			}
		}
	}
}
}
&#91;/sourcecode&#93;

In the final version of the class we've added a new public static function called setLabels. This will recursively loop through all of the forms children and check for any components for which a key has been set in our resource bundle file. If we find a value we'll check if it looks like something we can set the label for.

Here's an example of how we'd use this class

&#91;sourcecode language='jscript'&#93;
<mx:Metadata>
	[ResourceBundle("MyForm")]
</mx:Metadata> 

<mx:Script>
	<!&#91;CDATA&#91;
		import ResourceUtils;

		// call this function in the initializer handler for the component
		private function init():void
		{
			ResourceUtils.setLabels( this );
		}
	&#93;&#93;>
</mx:Script>

<mx:Form>
	<mx:FormItem>
		<mx:TextInput id="firstNameTextInput"/>
	</mx:FormItem>
	<mx:FormItem>
		<mx:TextInput id="lastNameTextInput"/>
	</mx:FormItem>
	...

</mx:Form>

If you scroll back to the top of the page you’ll see that we’ve removed the need to reference not just the ResourceManager but the labels as well.

In practice you’d probably want to use a combination of the static setLabels function as well instantiating an instance of ResourceUtils depending on whether it makes sense for the labels to be auto-magically mapped. Also, be careful to only use the setLabels functions on your lower level child components. If you used it in your main application class (and you have a large application) it’s going to recurse it’s way through every component in your application which probably won’t be too efficient.

Hope you find this approach useful

Best,
Hillel