The Space For App Developers

Beyond Plain Old HTML Objects

Archive for the ‘Swiz’ tag

Swiz 1.0 Command pattern API explained

with 9 comments

As you may know the Swiz team has released RC2 of the upcoming 1.0 version. Among many improvements and bugfixes there is one new feature that brings a built-in command pattern to Swiz. As part of my prep work for Adobe MAX where I will be speaking about AS3/Flex Frameworks I wanted to investigate it in more depth. As you can read in the Swiz docs, this implementation borrows heavily from the RobotLegs framework, which I will be also covering together with Parsley. This approach is really clean and simple but to be honest I kinda hope that it will evolve in future releases to be more metadata-driven, which is actually what Swiz is all about ;)

In the example below you can see it in action (right-click to view the full source code).


This movie requires Flash Player 10

So how do we get started?

If, when you read this post, Swiz is still in RC2, then you will need to grab the latest source code from github and build it yourself, the released RC2 has some quirks in regards to its command pattern implementation.

The first thing you do is create a SaveUserCommand class that implements the IEventAwareCommand interface and has one execute function. Obviously this is the function that will implement the command logic. It doesn’t accept any parameters, so if you need any you should provide them by injection. UPDATE 23.09.2010: Triggering event is available through event setter function when you implement IEventAwareCommand instead of ICommand.

package com.riaspace.max.swiz.commands
{
	import com.riaspace.max.swiz.events.UserEvent;
 
	import flash.events.Event;
 
	import mx.controls.Alert;
 
	import org.swizframework.utils.commands.IEventAwareCommand;
 
	public class SaveUserCommand implements IEventAwareCommand
	{
 
		private var _event:UserEvent;
 
		public function set event(value:Event):void
		{
			_event = value as UserEvent;
		}
 
		public function execute():void
		{
			Alert.show("SaveUserCommand executed, userName: " + _event.userName);
		}
	}
}

Next you can map/configure our command using a MyCommands class that extends the Swiz specific CommandMap. Just a hint here for the Swiz team that it would be cool to have the ability to configure commands directly in BeanProvider in future releases ;)

package com.riaspace.swiz.configs
{
	import com.riaspace.swiz.commands.SaveUserCommand;
	import com.riaspace.swiz.events.UserEvent;
 
	import org.swizframework.utils.commands.CommandMap;
 
	public class MyCommands extends CommandMap
	{
		override protected function mapCommands():void
		{
			// Mapping UserEvent.SAVE to SaveUserCommand class
			mapCommand(UserEvent.SAVE, SaveUserCommand, UserEvent, false);
		}
	}
}

Now you can instantiate the MyCommands class in the BeanProvider like any other bean:

<?xml version="1.0" encoding="utf-8"?>
<swiz:BeanProvider
	xmlns:fx="http://ns.adobe.com/mxml/2009"
	xmlns:swiz="http://swiz.swizframework.org" xmlns:commands="org.swizframework.utils.commands.*" xmlns:configs="com.riaspace.swiz.configs.*">
	<fx:Script>
		<![CDATA[
			import com.riaspace.swiz.models.presentation.MainPM;
		]]>
	</fx:Script>
 
	<fx:Declarations>
 
		<configs:MyCommands />
 
		<swiz:Prototype type="{MainPM}" />
 
	</fx:Declarations>
 
</swiz:BeanProvider>

At last you can dispatch the UserEvent.SAVE event in your PM class and the SaveUserCommand.execute will be called.

package com.riaspace.swiz.models.presentation
{
	import com.riaspace.swiz.events.UserEvent;
 
	import flash.events.IEventDispatcher;
 
	public class MainPM
	{
		[Dispatcher]
		public var dispatcher:IEventDispatcher;
 
		public function btnExecuteCommand_clickHandler():void
		{
			dispatcher.dispatchEvent(new UserEvent(UserEvent.SAVE, "John"));
		}
	}
}

Written by Piotr Walczyszyn

September 20th, 2010 at 6:41 pm

Swiz 1.0 Chaining API explained

with 8 comments

Yesterday I was tasked to find the best way to bootstrap a Flex application that uses Swiz 1.0 RC1. The challenge was to initialize application controllers and data model in the right sequence. This is an AIR application so it had to initialize data from an SQLite database, make few calls to remote services to check for data updates and do couple of other things before user could actually see first screen. My initial thought was to create a StartupController that would invoke the proper functions in a right order. That was a good initial direction but things started to get more difficult when I had to deal with async remote calls and local async APIs such as the one from the SQLite database.

So my next thought was, why not to try the new Chaining API that comes with Swiz? After my first look at the documentation I wasn’t really sure how I should approach it but after more digging I managed to figure it out ;) Here is what I did. I kept my StartupController with its init function that gets called after the controller is constructed and all its dependencies are injected. This was ensured by the [PostConstruct] metadata that comes with latest version of Swiz. Next that function creates a chains of function calls, remote invocations and events that bootstrap my application.

This is my StartupController where everything starts:

package controllers
{
	import delegates.RemoteServiceDelegate;
 
	import flash.events.IEventDispatcher;
 
	import mx.rpc.events.FaultEvent;
	import mx.rpc.events.ResultEvent;
 
	import org.swizframework.events.ChainEvent;
	import org.swizframework.utils.chain.AsyncCommandChainStep;
	import org.swizframework.utils.chain.ChainType;
	import org.swizframework.utils.chain.CommandChain;
	import org.swizframework.utils.chain.EventChain;
	import org.swizframework.utils.chain.EventChainStep;
	import org.swizframework.utils.chain.FunctionChainStep;
 
	public class StartupController
	{
		[Dispatcher]
		public var dispatcher:IEventDispatcher;
 
		[Inject]
		public var remoteServiceDelegate:RemoteServiceDelegate;
 
		[PostConstruct]
		public function init():void
		{
			// Initializing sequential CommandChain, that will stop on errors
			var commandChain:CommandChain = new CommandChain(ChainType.SEQUENCE, true);
			// Registering event listener when chain execution is complete
			commandChain.addEventListener(ChainEvent.CHAIN_COMPLETE, commandChainComplete);
			// Registering event listener in case chain execution fails
			commandChain.addEventListener(ChainEvent.CHAIN_FAIL, commandChainFail);
 
			// Adding async chain step that invokes remote AMF service
			commandChain.addStep(
				new AsyncCommandChainStep(
					remoteServiceDelegate.ping,
					["ping param value"],
					pingResultHandler,
					pingFaultHandler));
			// Adding function step that calls local function
			commandChain.addStep(new FunctionChainStep(this.localFunction));
 
			// Initializing parallel event chain that will be nested in command chain
			var eventChain:EventChain = new EventChain(dispatcher, ChainType.PARALLEL, true);
			// Registering event listener when event chain execution is complete
			eventChain.addEventListener(ChainEvent.CHAIN_COMPLETE, eventChainComplete);
			// Registering event listener in case chain execution fails
			eventChain.addEventListener(ChainEvent.CHAIN_FAIL, eventChainFail);
 
			// Adding event chain step that will dispatch INIT_PERSISTENCE event,
			// this event is mediated in PersistenceController
			eventChain.addEvent(new EventChainStep("INIT_PERSISTENCE"));
			// Adding event chain step that will dispatch INIT_SOMETHING_ELSE event,
			// this event is NOT mediated anywhere, this just for example and better understanding
			// eventChain.addEvent(new EventChainStep("INIT_SOMETHING_ELSE"));
 
			// Nesting event chain in parent command chain
			commandChain.addStep(eventChain);
			// Starting whole command chain
			commandChain.start();
		}
 
		private function localFunction():void
		{
			trace("Running localFunction");
		}
 
		public function pingResultHandler(event:ResultEvent):void
		{
			trace("Received result from remote ping call:", event.result);
		}
 
		public function pingFaultHandler(event:FaultEvent):void
		{
			trace(event.fault.faultDetail);
		}
 
		private function commandChainComplete(event:ChainEvent):void
		{
			trace("CommandChain complete");
		}
 
		private function eventChainComplete(event:ChainEvent):void
		{
			trace("EventChain complete");
		}
 
		private function commandChainFail(event:ChainEvent):void
		{
			trace("CommandChain failed");
		}
 
		private function eventChainFail(event:ChainEvent):void
		{
			trace("EventChain failed");
		}
	}
}

Below is my PersistenceController that will initialize the local database; its init function is called when the INIT_PERSISTENCE event is dispatched. When the database initialization is finished event.complete(); has to be called in order to proceed with chain execution:

package controllers
{
	import flash.events.IEventDispatcher;
 
	import org.swizframework.utils.chain.EventChainStep;
 
	public class PersistenceController
	{
		[Dispatcher]
		public var dispatcher:IEventDispatcher;
 
		[Mediate("INIT_PERSISTENCE")]
		public function init(event:EventChainStep):void
		{
			trace("Initializing PersistenceController...");
			event.complete();
		}
 
	}
}

This last snippet demonstrates the RemoteServiceDelegate class, which can be used to invoke remote services. It works with AsyncCommandChainStep:

package delegates
{
	import mx.rpc.AsyncToken;
	import mx.rpc.remoting.RemoteObject;
 
	public class RemoteServiceDelegate
	{
		[Inject]
		public var remoteObject:RemoteObject;
 
		public function ping(param:String):AsyncToken
		{
			return remoteObject.ping(param);
		}
	}
}

You can download my demo project source code from here; note that it doesn’t do anything except print out traces to the console. This zip contains a build of latest Swiz source code from GitHub that has some FunctionChainStep fixes.

Written by Piotr Walczyszyn

August 11th, 2010 at 3:51 pm

Posted in Examples

Tagged with ,