|
DefiningApplicationComponents
Defining Application ComponentsWith 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 |
Sign in to add a comment
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?
I'm interested in how Swiz would work with modules as well.
Here is autowireByType - hope that helps - in addition, we should cache the result of describeType:
Here is the caching code
Keep up the GR8 workI 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!
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.
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?
When I try to add:
to my Beans.mxml I get this error:
@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.
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> -----------------------------------Delegate missing on last example