Substitution Tokens in Resource Bundle Strings in Flex

There’s many times where you want to use a Resource Bundle String in-conjunction with variable data. People often breakup the RB String into 2 pieces like so:

part1=My name is
part2=and I like

And then put it back together in AS like:

var part1:String = ResourceManager.getInstance().getString('Labels', 'part1');
var name:String = "Brian Riley";
var part2:String = ResourceManager.getInstance().getString('Labels', 'part2');
var likes:String = "beer.";

var myStr:String = part1 + " " + name + " " + part2 + " " + likes;

Which works, but it’s a lot of unnecessary code. Take advantage of the tokens allowed in RBs and do the following:

likes=My name is {0} and I like {1}

var likes:String = ResourceManager.getInstance().getString('Labels', 'likes', ["Brian Riley", "beer."]);

The ResourceManager class will automatically substitue the tokens {0} and {1} in order and replace them with the array of strings provided as the last param in the getString() method.

Much simpler, right?

Post to Twitter Tweet This Post

, , , , ,

No Comments

WASI @ MAX!

Hit us up at MAX!

Brian Riley
617-480-0432
brianr@webappsolution.com
AIM/GIM/MSN/YIM/Twitter/Skype: wasibrianr

Tim McGee
508-566-6711
tim@webappsolution.com
AIM/GIM/YIM/MSN cheftimbob
Skype wasitim

Joe Seiter
201.981.2347
joes@webappsolution.com
YIM: seiter
AIM/GIM/Skype: wasijoes

Post to Twitter Tweet This Post

,

No Comments

Software List For New MacBook Pro Laptop

My partner and I just received our new MBPs and we’re pretty pumped about them — 17in, 8GB Ram, stateless HDs — very speedy. Startup is like 3 seconds, and I’m not kidding.

As we all know, getting a new computer is awesome, but it’s also a double edged sword…as my other partner Joe noted: “The worst thing about getting a new laptop is…well…getting a new laptop.” Sure the thing flies and all, but man there’s a good amount of hrs getting it ready for primetime development…think of all the little tools you’ve collected to make your laptop rock…

My partner and I have started to compile a live list of dev tools, goodies, IDEs,and whatevs we use on a regular basis and we thought we’d share — if you have others, please add to this list:

Basic Software & IDEs

Connectivity & Tools

IM & Social

Post to Twitter Tweet This Post

, , , , , , ,

1 Comment

Mac Flash Builder Plugin Install Error 6 + FIX

I just received my new 17in MBP so I pulled down a new version of Eclipse’s J2EE IDE and installed Flash Builder Plugin.

NOTE: You need to use the Eclipse Galileo Packages and the Carbon Install in order for this to work since FB is not compatible with the latets Eclipse build called Helio — here’s a link to the exact version of Eclipse that I used.

After a seemingly successful installation I received the following error code when starting up Eclipse: Error 6. I googled around a bit and found this link with this important tid-bit that fixed my issue:

  • Reboot your Mac
  • Navigate to your installation folder under /Adobe Flash Builder 4 Plug-in/install.support/AdobeFlashBuilderPluginSTIWrapperMac/
  • Run Install.app from that location, to make sure all the required runtimes are installed successfully.

The restart seemed to do nothing, but running the installer did the trick. Good luck!

Post to Twitter Tweet This Post

, , , , ,

No Comments

Flash Builder Network Monitor Project Property Causes HTTP Security Error

This sucker literally sucked up waaaay too many man hrs between my partner and myself trying to debug this issues, so I hope it saves someone else — for the short read, if you’ve used the Network Monitor in Flash Builder in a project make sure you open up .actionScriptProperties and set the property includeNetmonSwc = false.

includeNetmonSwc=”false”

For more of an explanation read the following — here’s the scenario:

We created an app that loads services.xml from within the same Java container as our SWF, so there’s no need for a crossdomain.xml file. The xml file was relative to the SWF and the HTML container for the SWF as such:

* main.html
* main.swf
* assets/service-locator/services.xml

In our local dev environments we have a Flex app that publishes itself to a Java app on Tomcat running in Eclipse with BlazeDS + Spring + Hibernate. Running locally, everything worked fine. When we deployed the WAR to a client’s remote Tomcat instance logged in via their VPN, my partner and I were able to hit the app and the XML file loaded fine. When the client tried, it failed and they received the following HTTPService Fault:

  • faultCode = Channel.Security.Error
  • faultDetail = Destination: DefaultHTTP
  • faultString = Security error accessing url

This honestly baffled us for some time…why could we hit get the SWF to load the XML file when VPN’d in while the client received an error on their own network? After trying every security thing under the sun with the Flash Player, we simply created a new Flex project, put in a simple XML load via an HTTPService, and deployed it to the same WAR (as above with BlazeDS + Spring + Hibernate) and it worked for both us and them. Then we put the exact same Flex code from our original app into the test Flex app and again it worked for both us and out client…huh?

While it did work and we could just move on and fist pump the day away with out newfound success, we were obviously worried that something greater was at play and might come back to bite us in the ass. At this point we knew it was client code and not server code, so we decided to check the build properties and compiler options, etc, but it was all the same.

Finally we decided to do a compare on the project property files from the original project and the new one starting with .actionScriptProperties — and there it was, a simple flag difference of the property:

includeNetmonSwc=”true”

…and WHAM, it smacked me in the face.

Waaaaay back when we started this project I decided to play with the new Network Monitor (NM) integrated into Flash Builder. I used it maybe for 30 mins and said, ok, great…next. Well, this little flag apparently creates some settings (maybe opens ports or something) on dev machines that allows the NM to work like an HTTP Proxy/Sniffer similar to Charles or ServiceCapture…when this SWF is deployed to a server where these settings haven’t been set…well…you’re efffed. No dice. And you get the security error mentioned above.

Bottom line, make sure you either set the value to false:

includeNetmonSwc=”false”

or remove this sucker entirely. I really hope that helps someone as it cost us a ton of time. Thank you Adobe.

Post to Twitter Tweet This Post

, , , , , ,

5 Comments

Flex & ActionScript Regex List

I’m not great with regex since I don’t use it a ton so I constantly find myself looking up simple, reusable regex statements…thought I’d just start listing them as I use them in case someone else finds them useful. This will start with just a couple examples that we’ll continue to add to.

Assume the following vars:

var myString:String;
var pattern:RegExp;
var resultString:String;
var isSuccess:Boolean;

Remove All Spaces

myString = "The quick brown fox.";
pattern = /\s+/g;
resultString = myString.replace(pattern, "");
trace(resultString); // Thequickbrownfox.

Remove Special Characters & Spaces

myString = "The 123 quick 456 brown !@#$% fox.";
pattern = /\W/g;
resultString = myString.replace(pattern, "");
trace(resultString); // The123quick456brownfox

Remove Special Characters & Numbers & Spaces

myString = "The 123 quick 456 brown !@#$% fox.";
pattern = /[^a-zA-Z]+/g;
resultString = myString.replace(pattern, "");
trace(resultString); // Thequickbrownfox

Test URL String

myString = "http://yahoo.com";
pattern = /^http(s)?:\/\/((\d+\.\d+\.\d+\.\d+)|(([\w-]+\.)+([a-z,A-Z][\w-]*)))(:[1-9][0-9]*)?(\/([\w-.\/:%+@&=]+[\w- .\/?:%+@&=]*)?)?(#(.*))?$/i;
isSuccess = pattern.test(myString);
trace(isSuccess); // true
myString = "htt://yahoo.com";
isSuccess = pattern.test(myString);
trace(isSuccess); // false

Trim String (With Tabs & Returns): Courtesy of Jeff Channel

myString = myString = "The quick brown fox.                   		";
pattern = /^\s+|\s+$/gs;
resultString = myString.replace(pattern, "");
trace(resultString); // "The quick brown fox."

Post to Twitter Tweet This Post

, , ,

4 Comments

Set Flex to Focus on Application Load

By default a Flex application is not in focus in the browser when it loads. This can be especially frustrating if you have say a login view and would like the username field to be in focus when the application starts cranking. However, with some simple JavaScript we can set the Flash object to focus when the application loads.

NOTE: This will not work in Safari, Chrome, or any other browser that leverages Webkit, as it doesn’t allow the focus to be set on embedded objects. If you’re targeting IE and Firefox the proposed solution below will work.

For example purposes, let’s assume we want to make our username TextInput field have focus when the Flex app starts rocking.

First, set the focus to the username field — assume a ViewMediator is doing this:

this.view.userNameTextInput.setFocus();

Create a simple JavaScript method to set the focus of the browser to your Flex application. Create this method in the index.template.html file in your Flex project — just drop it right before the closing HTML tag at the bottom of the file:

<script type="text/javascript">
function onFlexInitialized()
{
	//alert("onFlexInitialized");

	<!-- Force the browser to set flex app with focus -->
	document.getElementById("${application}").focus();
}
</script>

Next catch the application complete event and call the JS method we just created — I put in how to remove the event handler for the app complete for both Flex 3 and 4:

/**
 * Constructor.
 */
public function AppController()
{
	FlexGlobals.topLevelApplication.addEventListener(FlexEvent.APPLICATION_COMPLETE, onAppComplete); // Flex 4
        //Application.application.addEventListener(FlexEvent.APPLICATION_COMPLETE, onAppComplete); // Flex 3
}

/**
 * Handles the application complete event.
 */
protected function onAppComplete(e:FlexEvent):void
{
	FlexGlobals.topLevelApplication.removeEventListener(FlexEvent.APPLICATION_COMPLETE, onAppComplete); // Flex 4
        //Application.application.removeEventListener(FlexEvent.APPLICATION_COMPLETE, onAppComplete); // Flex 3

	if(ExternalInterface.available)
	{
		ExternalInterface.call("onFlexInitialized");
	}
}

And that’s it. Again, it doesn’t work in Safari and Chrome. Here are the 2 defects logged for each brower’s bug-base respectively: Chrome, Safari.

Post to Twitter Tweet This Post

, , , , ,

7 Comments

New AbstractViewMediator For Popups Using Swiz 1.0 RC1

This post builds on my last post regarding the use of the ViewMediator pattern with Swiz. One of the things I hadn’t tested when I wrote the original AbstractViewMediator (AVM) was popups. Once I started using this base class in a couple projects I realized I had a pretty big hole that I quickly need to fix.

Download the new and improved AbstractViewMediator.

Normally, views are added to the display list, but popups are actually added to the SystemManager so they don’t fire off the flash.events.Event.ADDED_TO_STAGE event that’s mediated in the setView() method in my AbstractViewMediator and that’s the core piece of logic behind the entire AbtractViewMediator. See the last post for details.

So how would I connect my views to my VMs? The key was to make the AbstractViewMediator actually create the popup via the following method:

public function createPopupView(
								view:IFlexDisplayObject,
								parent:DisplayObject,
								modal:Boolean = false,
								childList:String = null,
								moduleFactory:IFlexModuleFactory = null
								):IFlexDisplayObject
{
	this.setView(view);

	PopUpManager.addPopUp(view, parent, true); // window:IFlexDisplayObject,parent:DisplayObject,modal:Boolean = false,childList:String = null,moduleFactory:IFlexModuleFactory = null
	PopUpManager.centerPopUp(view);

	return view;
}

If you look at the method signature you’ll see that I pass in a reference to the view we’re creating and then call the all important public function setView(value:*):void method that’s usually called by the mediated event flash.events.Event.ADDED_TO_STAGE. This is what creates the connection between a VM and a View. This also brings up a new question — who calls this method?

In order for the VM to be hooked up to the correct view, we’ll need to make sure we call the createPopupView(...) method on our concrete VM implementation…hmm…so how do we get a hold of that…ahh yes, the nice guys at Swiz gave us access to it via the list of beans that this instance of Swiz has. Let’s learn by example.

We’ll imagine we need to open a popup from MainView after clicking the “Get Users Button”. Naturally, our MainViewMediator mediates the button’s click event and calls this functional method:

/**
 * Open the create user view.
 */
public function openCreateUserView():void
{
	var view:CreateUserView;
	var vm:CreateUserViewMediator;

	view = new CreateUserView();
	vm = this.getViewMediatorBeanById("createUserViewMediator") as CreateUserViewMediator;

	vm.createPopupView(view, this.view, true);
}

The key line is where we get a handle to the CreateUserViewMediator via the method getViewMediatorBeanById(...) in the AbstractViewMediator:

/**
 * Helper method that returns a view mediator managed by Swiz by it's ID.
 *
 * @param	id	The unique ID for the view mediator as defined in the BeanLoader.
 */
public function getViewMediatorBeanById(id:String):AbstractViewMediator
{
	var vm:AbstractViewMediator = this.swiz.beanFactory.getBeanByName(id).source as AbstractViewMediator;

	return vm;
}

/**
 * Helper method that returns a view mediator managed by Swiz by it's ID.
 *
 * @param	clazz	The class type for our view mediator.
 */
public function getViewMediatorBeanByType(clazz:Class):AbstractViewMediator
{
	var vm:AbstractViewMediator = this.swiz.beanFactory.getBeanByType(clazz).source as AbstractViewMediator;

	return vm;
}

This method in the AVM also assumes it has a handle to the instance of Swiz that contains the beans. This is done by implementing the ISwizAware interface in the AVM.

/**
 * Creates a reference to the instance of Swiz this bean is in. This
 * allows us to get a hold of manager beans by using:
 * <code>var vm:MyViewMediator = swiz.beanFactory.getBeanByName("myViewMediator").source as MyViewMediator;</code>
 */
public function set swiz(swiz:ISwiz):void
{
	this._swiz = swiz;
}
public function get swiz():ISwiz
{
	return this._swiz;
}
protected var _swiz:ISwiz;

There you have it. All the pieces. If you create popups this way you’ll get the expected VM to View connection.

Download the new and improved AbstractViewMediator.

Post to Twitter Tweet This Post

, , , , , ,

5 Comments

WASI AIR Logging Console v0.1.3.0 & Logging Util Tools UPDATE

Introduction

While there are several updates and changes I made to the underlying implementation, the overall functionality remains the same so please read my original post WASI AIR Logging Console & Logging Util Tools for a general overview.

See It In Action!

  • Logging Test Application — Small test app that uses the logging framework and spits out log statements every second. I have a link to the console below for quick testing.
  • Logging Console Test Application — Allows you to see the Console in action when you run it and the Logging Test Application at the same time.

Assets

Updates & Changes

  • The name of the SWC has changed from WASILogConsoleLibrary.swc to  WASILoggingLibrary.swc.
  • The LocalConnection Logger is no longer a wrapper class, but now an actual logging target called LocalConnectionTarget.
  • Added a Firebug (FireFox extension) Logging Target that outputs the same log message as the LocalConnectionTarget.
  • Created a base WASILoggingTarget with several, common helper methods for creating targets.

Since I mentioned that some of the actual impl changed a bit I’m going to show some code illustrating the changes.

Setting Up in Main App :: Example Code

The difference between this one and last version I shared has to do with the use of targets as opposed to a wrapper class. I also put all of my logging specifics into a helper class for the Test App called Logging — I could’ve just as easily put this all into the main app file, but I decided to move it into a new class for better organization.

<mx:Application
	xmlns:mx="http://www.adobe.com/2006/mxml"
	creationComplete="this.init()">

	<mx:Script>
		<![CDATA[
			import com.webappsolution.controller.FilterControllerTest;
			import com.webappsolution.delegate.FilterDelegateTest;
			import com.webappsolution.logging.Logging;

			private static var loggerSetup:* = Logging.setupLogging();

			private function init():void
			{
				var filterDelegateTest:FilterDelegateTest = new FilterDelegateTest();
				var filterControllerTest:FilterControllerTest = new FilterControllerTest();
			}

		]]>
	</mx:Script>

</mx:Application>

The line to make note of is

private static var loggerSetup:* = Logging.setupLogging();

which calls my helper Logging class that actually creates the logging targets, levels, and filters.

Helper Logger Class (Not Required)

/**
 * Web App Solution Confidential Information
 * Copyright 2010, Web App Solution, Inc.
 *
 * @date Apr 26, 2010
 */
package com.webappsolution.logging
{
	import com.webappsolution.logging.LoggingUtils;
	import com.webappsolution.logging.target.FirebugTarget;
	import com.webappsolution.logging.target.LocalConnectionTarget;

	import mx.logging.LogEventLevel;
	import mx.logging.targets.TraceTarget;

	public class Logging
	{
		/**
		 * Create your list of targets here.
		 */
		public static function setupLogging():void
		{
			var level:int = LogEventLevel.ALL;
			var filters:Array = createFilters();
			var targets:Array = createTargets();

			// set up logging for the application
			LoggingUtils.setupLogging(level, filters, targets);
		}

		/**
		 * Create your list of compile-time filters here.
		 */
		public static function createFilters():Array
		{
			var filters:Array = ["*"];

			return filters;
		}

		/**
		 * Create your list of targets here.
		 */
		public static function createTargets():Array
		{
			var traceTarget:TraceTarget;
			var firebugTarget:FirebugTarget;
			var lcTarget:LocalConnectionTarget;
			var targets:Array;

			traceTarget = new TraceTarget();
			traceTarget.level = LogEventLevel.ALL;
			traceTarget.includeDate = true;
			traceTarget.includeTime = true;
			traceTarget.includeCategory = true;
			traceTarget.includeLevel = true;

			firebugTarget = new FirebugTarget();
			firebugTarget.level = LogEventLevel.ALL;
			firebugTarget.includeDate = true;
			firebugTarget.includeTime = true;
			firebugTarget.includeCategory = true;
			firebugTarget.includeLevel = true;

			lcTarget = new LocalConnectionTarget();
			lcTarget.level = LogEventLevel.ALL;
			lcTarget.includeDate = true;
			lcTarget.includeTime = true;
			lcTarget.includeCategory = true;
			lcTarget.includeLevel = true;

			// create a list of targets
			targets = [traceTarget, firebugTarget, lcTarget];

			return targets;
		}
	}
}

Here I’m just creating the specific logging targets for my application:

  • TraceTarget — ouputs to your Flex IDE’s console
  • FirebugTarget — ouputs to your FireFox’s Firebug plugin’s console
  • LocalConnectionTarget — ouputs to WASI AIR Logging Console

The other change to make note of is the actual definition of the logger in each class you’re implementing logging. Let’s take a quick look here.

New Logger Instantiation

The OLD way was like this:

private static const logger:ILogger = LocalConnectionLog.getLogger(FilterControllerTest);;

The NEW way was like this:

private static const logger:ILogger = Log.getLogger(LoggingUtils.getFullyQualifiedClassName(FilterControllerTest));

The new design decouples the logging from our custom wrapper class LocalConnectionLog and now uses the out of the box mx.logging.Log to create a category for each logger instantiation. Notice the use of LoggingUtils.getFullyQualifiedClassName(clazz:Class):String to get the fully qualified string name for the object; one can also just pass in a string category of their choice instead of using the class name.

Post to Twitter Tweet This Post

, , , , ,

1 Comment