AS3: Keep your UI's DRY
Consider the following code snippet. Real world code, a form with multiple icons: //contactIconBtn var contactIconBtn:Sprite = _skin.getAssetAs("contactIconBtn", Sprite, null ); _contactIconBtn = new OSIconButton(contactIconBtn); _contactIconBtn.addEventListener(InteractiveContainer.ON_CLICK, _hanldeButtons );
//whatIsIconBtn => window_info:
var whatIsIconBtn:Sprite = _skin.getAssetAs("whatIsIconBtn", Sprite, null );
_whatIsIconBtn = new OSIconButton(whatIsIconBtn);
_whatIsIconBtn.addEventListener(InteractiveContainer.ON_CLICK, _hanldeButtons );
//twitterIconBtn
var twitterIconBtn:Sprite = _skin.getAssetAs("twitterIconBtn", Sprite, null );
_twitterIconBtn = new OSIconButton(twitterIconBtn);
_twitterIconBtn.addEventListener(InteractiveContainer.ON_CLICK, _hanldeButtons );
//facebookIconBtn
var facebookIconBtn:Sprite = _skin.getAssetAs("facebookIconBtn", Sprite, null );
_facebookIconBtn = new OSIconButton(facebookIconBtn);
_facebookIconBtn.addEventListener(InteractiveContainer.ON_CLICK, _hanldeButtons );
Ugly, how to keep it DRY? Let's first look at this other snippet, which is the same but refactored.
var icons:Array = _skin.locateChildrenBySuffix(/(^.+)(IconBtn)/i, Sprite, -1);
var btn:OSIconButton;
for each( var skin:Sprite in icons ) {
btn = new OSIconButton(skin);
btn.addEventListener(InteractiveContainer.ON_CLICK, _hanldeButtons );
this["_" + skin.name] = btn;
}
Much nicer, ain't it!
Now, for this to work we need to have in place some conventions. This is how I handle the buttons. Notice how there is a single method handling all buttons and discriminating by the button's name:
private function _hanldeButtons(e:Event):void {
switch(e.target.name ) {
case _contactIconBtn.name:
_windowManager.openWindow( WindowContact.ID, false );
break;
case _whatIsIconBtn.name:
_windowManager.openWindow( WindowInfo.ID, false );
break;
case _facebookIconBtn.name:
var link:LinkVO = ModelList.instance.siteModel.getLink( "facebookUrl");
link.execute( );
break;
case _twitterIconBtn.name:
link = ModelList.instance.siteModel.getLink( "twitterUrl");
link.execute( );
break;
}
}
The important method of _skin:
/**
* @inheritDoc
*/
public function locateChildrenBySuffix(suffix:*, type:Class , index:int = 0 ):* {
var collection:Array = ContainerUtils.collect( _source, suffix );
if ( collection.length == 1 ) return collection[0] as type;
else if ( collection.length > 1 && index == -1 ) return collection;
else return collection[index] as type;
}
The call to ContainerUtils.collect( _source, suffix );
handles the actual gathering of items:
/**
*
* @param container
* @param mask
* @return
*/
public static function collect( container:DisplayObjectContainer, mask:* ):Array {
var reg:RegExp = mask is RegExp ? mask : new RegExp( mask );
var n:int = container.numChildren;
var c:DisplayObject;
var id:String;
var childs:Array = [];
while (n-- > 0 ) {
c = container.getChildAt(n);
id = c.name;
if ( id.match( reg ) ) childs.push( c );
}
return childs;
}