My favorites | Sign in
Project Logo
                
Search
for
Updated Sep 21, 2008 by Chris.Scott.One
DefiningApplicationComponents  

Defining Application Components

With your project set up, it's time to tell Swiz about your application components. Swiz's primary goal is to assist you in building Flex applications in a true MVC paradigm. Your application will need RemoteObjects, utilities, data collections, commands, events, etc, to function. Your views, in turn, will need to access all of these. Swiz provides a very simple way to encapsulate components into Controllers, which are automatically provided to the Views that need them.

We'll start by defining some components for our application, defined in a custom BeanLoader. BeanLoader is a special Swiz utility for loading components into it's central factory. Create a new mxml component somewhere in your source directory and name it 'Beans.mxml'. By default this will be based on Canvas, we'll switch it to Swiz's BeanLoader class. Change the contents of your new Beans.mxml file to look like the following:

<?xml version="1.0" encoding="utf-8"?>
<BeanLoader xmlns="org.swizframework.util.*" xmlns:mx="http://www.adobe.com/2006/mxml">

</BeanLoader>

We'll add a RemoteObjects and also a DynamicChannelSet, which enables us to remove the dependency on services-config.xml:

<BeanLoader xmlns="org.swizframework.util.*" xmlns:mx="http://www.adobe.com/2006/mxml">
	<!-- custom channel set -->
	<DynamicChannelSet id="myAmfChannel">
		<serverPort>8080</serverPort>
		<contextRoot>/demo-web</contextRoot>
	</DynamicChannelSet>
		   
	<!-- user service --> 
	<mx:RemoteObject id="userService" 
				  destination="userService" 
				  channelSet="{myAmfChannel}"/>
</BeanLoader>

When our application loads, we want to tell Swiz to process this BeanLoader, in order to create instances of these components. Swiz will store them in an internal factory. We can accomplish by adding a function in our application's main view, in an mx:Script block. We''ll create an onPreinitialize function, which I will attach to the main application's preinitialize event.

<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" 
			layout="absolute" width="100%" height="100%"
			preinitialize="onInitialize()">

	<mx:Script>
    	<![CDATA[
		import org.swizframework.Swiz;

		private function onInitialize() : void {
			// load up swiz beans
			Swiz.loadBeans( [ Beans ] );
		}
	]]>
     </mx:Script>

	<!-- rest of view -->

</mx:Application>

Because Swiz ties itself into your applications creation life cycle, in order to autowire your views, you want Swiz to initialize as early as possible, the preinitialize event is before any views are added to the stage. You then pass in a list of classes to loadBeans that are the various BeanLoaders you may have created. Notice the way that the class is passed in, similarly to the way PopupManager works. You are not passing in an instance, Swiz will handle loading everything for you. You are free to create as many BeanLoaders as you like for organization. I tend to create separate loaders for my RemoteObjects, Swiz will find all your components when it comes to autowire everything.

Let's add some more interesting things to our BeanLoader.

<BeanLoader xmlns="org.swizframework.util.*" xmlns:mx="http://www.adobe.com/2006/mxml">
	<!-- custom channel set -->
	<DynamicChannelSet id="myAmfChannel">
		<serverPort>8080</serverPort>
		<contextRoot>/demo-web</contextRoot>
	</DynamicChannelSet>
		   
	<!-- user service --> 
	<mx:RemoteObject id="userService" 
				  destination="userService" 
				  channelSet="{myAmfChannel}"/>
	
	<!-- user controller -->
	<controller:UserController id="userController"/> 
</BeanLoader>

Our UserController is going to use the RemoteObject to make remote calls to our backend services. In order to tell Swiz about this relationship, we need to add a property to the UserController for the RemoteObject, which we have given the id "userService" in our BeanLoader.

public class UserController extends AbstractController
{
		
	[Autowire(bean="userService")]
	public var userService : RemoteObject;

	public function UserController() { }

	// rest of class...
}

You'll notice that we have defined a local variable of type RemoteObject, which we have annotated with Swiz's Autowire metadata. The Autowire is used to define the bean name, which is the id we used in the BeanLoader, where the dependent object can be found. When Swiz processes your BeanLoaders, it caches all the objects they contain in a central factory and interrogates them to find their Autowire metadata elements. If Swiz can find a bean in its local cache matching the bean name you provided, it will inject it into the variable where the Autowire metadata is defined. Because the userController above requires the userService to function, we call it a dependency. When Swiz loads all your beans, it injects their dependencies for you. We call this Dependency Injection, it's the primary purpose if Swiz's internal factory. When you later ask Swiz for one of your components, you can be assured that as long as you defined each of its dependencies somewhere in a BeanLoader, the component will be returned to you all wired up and ready to use. Although this may seem like a round about way to create your application components, there are tremendous benefits to the approach. By externalizing dependency management, your components are only concerned with their primary business logic. If you get into the habit of using interfaces, and only program to the interfaces of your components' dependencies, you gain the ability to swap out implementation with very little effort, by simply altering your bean definitions in your BeanLoaders. Although a full discussion of Inversion of Control is outside the scope of this getting started document, if you are new to IoC, I would strongly suggest looking over some of the links provided in the Resources section.

Now that we have defined some components lets move on to making Async requests through your RemoteObject and how Swiz can make this simpler.

Next: SwizControllers


Comment by paulo.quintans, Aug 18, 2008

This is a wonderful job. I like very much the idea of this framework. Still, browsing through the code, I wasn't clear how this framework would behave, when working with modules. Do the beans get injected, when loading and unloading modules?

Comment by tylerChesley, Aug 27, 2008

I'm interested in how Swiz would work with modules as well.

Comment by mraad23, Oct 15, 2008

Here is autowireByType - hope that helps - in addition, we should cache the result of describeType:

private function autowireByType(
obj:Object, depends : XML
) : void {
const type : String = depends.@type; const reference : Object = findObjectByType( type ); if( reference != null ) {
objdepends.@name = reference;
}
}

private function findObjectByType( type : String ) : Object {
for each ( var obj : Object in allBeans ) {
var xmlDescription : XML = describeType/Cache/( obj ); if( type == xmlDescription.@name ) {
return obj;
} for each ( var clazz : XML in xmlDescription.extendsClass ) {
if( type == clazz.@type ) {
return obj;
}
} for each ( var interfaze : XML in xmlDescription.implementsInterface ) {
if( type == interfaze.@type ) {
return obj;
}
}
} return null;
}

Here is the caching code

private static const m_cache : Dictionary = new Dictionary();

private function describeTypeCache( obj : ) : XML {
var result : Object = ObjectUtil?.getClassInfo( obj ); var xmlDescription : XML = m_cacheresult.name? as XML; if( xmlDescription == null ) {
xmlDescription = describeType( obj ); m_cacheresult.name? = xmlDescription;
} return xmlDescription;
}
Keep up the GR8 work

Comment by ypicout, Oct 20, 2008

I think it would be a great idea to let the user decide to use autowiring or not. Indeed, in my application, the use of swiz dramatically reduces the performance. The fault to Application.application.addEventListener(Event.ADDED_TO_STAGE, handleAutowireEvent, true); in swiz.as. I have some complex components dynamically added. With Swiz autowiring , they are displayed within 12 seconds. Without Swiz they are displayed in 2 seconds.

I did not try mraad23 caching method from, maybe it can help.

Anyway, great job!

Comment by russell.centanni, Nov 05, 2008

mraad23, there are classes provided (mx.utils.DescribeTypeCache?, and mx.utils.DescribeTypeCacheRecord?) that take care of caching the result of describeType. It doesn't show up in Flex Builder's auto completion for some reason, but if you put the imports there, the compiler will find them. Hope that helps.

Comment by ed.eustace, Nov 09, 2008

Hello, I was wondering if its possible to define constructor arguments for objects in the BeansLoader?? Or is it better to remove constructor arguments and use the autowire metadata tag?

Comment by dave.babbitt, May 21, 2009

When I try to add:

<controller:UserController id="userController"/>

to my Beans.mxml I get this error:

Severity and Description Path Resource Location Creation Time Id
The prefix "controller" for element "controller:UserController?" is not bound. MyFirstSwizProject?/src Beans.mxml line 14 1242908776703 5460

Comment by cliff.meyers, Jun 09, 2009

@ed: items declared via MXML must have a null argument constructor, so you'll want to use Autowire? on your public properties.

@dave: you are missing the xmlns:controller="com.foo.controller." declaration that maps an XML namespace to a package in MXML.

Comment by tarun.telang, Oct 01, 2009

Comment by tarun.telang, Oct 16, 2009

Below is my Hello World Example created for demonstrating how to structure your application as per MVC architecture

Main.mxml (Flex Application):
----------------------------------------
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"	
	xmlns:swiz="http://swiz.swizframework.org" 
	xmlns:swizframework="org.swizframework.*" 
	xmlns:views="views.*">
	
	<mx:Script>
		<![CDATA[
			import mx.logging.LogEventLevel;
			import mx.rpc.events.FaultEvent;
			
			private function genericFault(event:FaultEvent): void {
				
			}
		]]>
	</mx:Script>
	
	<swiz:SwizConfig	
		beanLoaders="{[Beans]}"
		
	/>
	
	<views:HelloWorld/>		
	
</mx:Application>



----------------------------

Beans (MXML Component - Beans.mxml)

----------------------------
<?xml version="1.0" encoding="utf-8"?>
<BeanLoader xmlns="org.swizframework.util.*" 
           xmlns:mx="http://www.adobe.com/2006/mxml" 
           xmlns:controllers="controllers.*" 
           xmlns:delegates="delegates.*"
           xmlns:models="models.*"
           >

		   <!-- Services --> 
           <mx:HTTPService id="helloService" url="hello.xml"/>
	
           <!-- Controller -->
           <controllers:HelloController id="helloController"/>
           
           <!-- Delegate -->
           <delegates:HelloDelegate id="helloDelegate"/>
           
           <!-- Model -->
           <models:Hello id="hello"/>
</BeanLoader>
================================

Model (ActionScript Class - models\Hello.as)
-----------------------
package models
{
	import controllers.HelloController;
	
	public class Hello
	{		
		[Bindable]				
		public var message:String;
		
		public function Hello()
		{
			trace("initializing Hello model..");
		}

	}
}

=======================================

Controller (ActionScript Class - controllers\CommonAbstractController.as & controllers\HelloController.as)
--------------------------------
package controllers
{
	import mx.controls.Alert;
	import mx.rpc.events.FaultEvent;
	
	import org.swizframework.controller.AbstractController;
	import org.swizframework.factory.IInitializingBean;

	public class CommonAbstractController extends AbstractController implements IInitializingBean
	{
		public function CommonAbstractController()
		{
		}
		
		public function initialize():void
		{
		}
	
		protected function handleFault(event : FaultEvent) : void {
	    	if (event.message.toString() != null && event.message.toString() != '' ) {
	    		Alert.show(event.message.toString());
	    	}	        	
		}
      }
}
-------------------------------------

package controllers
{
	import delegates.HelloDelegate;
	
	import models.Hello;
	
	import mx.rpc.events.ResultEvent;
	
	public class HelloController extends CommonAbstractController
	{
		[Autowire(bean="helloDelegate")]
		public var helloDelegate:HelloDelegate;		
		
		[Autowire(bean="hello")]
		public var hello:Hello;
				
		public function HelloController()
		{
			trace("initializing Hello controller..");
			super();
		}
		
		public function getHelloMessage() : void {
			trace("calling getHelloMessage ...");
			executeServiceCall(helloDelegate.callHelloService(), handleHelloService, handleFault);
		}
		
		private function handleHelloService(event:ResultEvent):void{
			trace("calling handleHelloService ...");
			hello.message = event.result.message.hello;	
			trace( event.result.message.hello);				
		}
	}
}

--------------------------------
===========================================================
View (MXML Component - views\HelloWorld.mxml): 
-----------------------
<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" width="400" height="300">
	<mx:Script>
		<![CDATA[
			import mx.controls.Alert;
			
			import controllers.HelloController;
			import models.Hello;
			
			import mx.rpc.events.ResultEvent;			
			
			[Autowire(bean="helloController")]
			public var helloController:HelloController;
			
			[Bindable]
			[Autowire(bean="hello", property="message")]
			public var helloString:String;
			
			private function sayhello():void{				
				trace("calling sayhello ...");
				helloController.getHelloMessage();													
			}			
		]]>
	</mx:Script>
	<mx:HBox>
		<mx:TextInput text="{helloString}" />
		<mx:Button label="Click Me" click="sayhello()"/>
	</mx:HBox>
</mx:Canvas>
-----------------------------------
Comment by tomascaraccia, Oct 28, 2009

Delegate missing on last example


Sign in to add a comment
Hosted by Google Code