Archive for category swiz
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.
Swiz Passive View Example :: Part 2
Introduction
After implementing Part 1 of the Swiz + Passive View (PV) Example, I immediately decided that one thing I missed from the original Spring ActionScript (SAS) version was the ability to create services in an XML file that’s loaded at runtime, thus allowing you to take your app from one environment to another and simply change the XML as opposed to recompiling. Some devs don’t seem to care about this, but I find it quite useful and so do many of my clients so I created a simple DynamicServiceLocator (SL) class and “injected” it into my Example.
Tutorial Goal: Create a Dynamic Service Locator that Instantiates Services from an External XML File at Runtime
Assets
Acronyms
- PV = PassiveView
- SAS = Spring ActionScript
- SL = Service Locator
- VM = View Mediator
- CG = Cairngorm
- IoC = Inversion of Control
- DTO = Data Transfer Object
Caveats
Please read the previous posts in this series to get up to speed:
I won’t go into the details of what Swiz is and how it works as I’d like to let the code speak for itself and you can just hit up the Swiz homepage and view their simple examples; that said, I’ll point out several things I really like about Swiz.
I am using Swiz 0.6.4, as I found issues with the 1.0.0 alpha release (that I’m going to bring up with Ben Clinkenbeard).
Getting Started
The idea is simple:
- Create an XML file that defines all of the services leveraged in the app.
- Add the
DynamicServiceLocatorto theBeanLoader(Beans.mxml) and give it the URL to load the services XML file. - Listen to the
DynamicServiceLocator’s complete event, indicating that the services are loaded and parsed, and start the app up. - Inject the SL into the Delegates as opposed to injecting the services directly (that were previously defined and hardcoded in the
BeanLoaderfrom Part 1). - In the Delegate, request a service from the
DynamicServiceLocatorvia an ID that was set in the service’s definition in the XML file. - Use the service.
Let’s take it piece by piece…
Step 1: Create Services XML File
Let’s start by creating an XML file called services.xml and putting it in src/assets/service-locator/. Next, let’s add some services — truth be told, I took the format for the XML that defines a service directly from the idea behind the application context files used in SAS and then created my own Object Factory…it’s very simple mind you b/c I didn’t need a huge object factory taking up a ton of memory when the only objects it would ever create were 8 different services.
<?xml version="1.0" encoding="utf-8"?> <objects> <!-- =================================================================== --> <!-- SERVICE LOCATOR --> <!-- =================================================================== --> <!-- =================================================================== --> <!-- This application context defines multiple test configurations, it can be expanded to include a deployment configuration as well, please see comments below. 1. Local Testing: XML Services 2. Dev Server Testing: XML Services 3. Deployment: XML Services --> <!-- =================================================================== --> <!-- =================================================================== --> <!-- 1. LOCAL TESTING: XML Services --> <!-- =================================================================== --> <object id="loginService" class="mx.rpc.http.mxml.HTTPService"> <property name="url" value="assets/xml/login.xml" /> <property name="resultFormat" value="e4x" /> <property name="method" value="GET" /> </object> <object id="employeeService" class="mx.rpc.http.mxml.HTTPService"> <property name="url" value="assets/xml/employee_list.xml" /> <property name="resultFormat" value="e4x" /> <property name="method" value="GET" /> </object> </objects>
The idea is to create a list of services as object nodes. Start by giving each <object> node a unique id attribute, as this is the key we’ll use to request the service later. The following line defines the loginService:
<object id="loginService" class="mx.rpc.http.mxml.HTTPService">
Next, define the concrete Flex service type in the class attr — right now the service class must be one of the following types:
mx.rpc.http.mxml.HTTPServicemx.rpc.http.HTTPServicemx.rpc.remoting.mxml.RemoteObjectmx.rpc.remoting.RemoteObjectmx.rpc.soap.mxml.WebServicemx.rpc.soap.WebService
Finally, add any additional properties you want to define for the service as <property> nodes with the name and value attrs matching properties that are available for the service type. The following line adds the url property for the HTTPService:
<property name="url" value="assets/xml/login.xml" />
Step 2: Add DynamicServiceLocator to the BeanLoader
We’ll add the SL to the BeanLoader just like we did for all the other objects we want managed by the IoC Container, except we’ll also provide her with the URL to our services.xml file that we just created. When the url property is set on the SL, it will automatically load the services, parse the XML, instantiate a service for each <object> node it finds that matches one of the aforementioned service classes, and then adds that service to it’s hash or Dictionary object’s list of services via the id as it’s key.
<service:DynamicServiceLocator id="serviceLocator" eventDispatcher="{this.dispatcher}" url="assets/service-locator/services.xml" />
The other thing to make note of is that we’re again passing a reference to the BeanLoader’s dispatcher so the SL can broadcast events to other objects in the Swiz IoC Container.
Step 3: Listen to the SL’s Services Load Complete Event
Since this is more of an application level event, we’ll make the ApplicationController object handle this event:
[Mediate( event="serviceLocatorServicesLoadComplete" )]
public function onServiceLocatorLoadComplete():void
{
logger.debug("onServiceLocatorLoadComplete");
this.eventDispatcher.dispatchEvent(new DynamicEvent("showMainView"));
}
Which then in turn broadcasts a “showMainView” event that’s handled by the ApplicationViewMediator and ultimately removes a simple preloader (which is there in case the SL should take any significant amount of time to load and parse — unlikely here, but useful in the future) and adds the MainView to the stage.
[Mediate( event="showMainView" )]
public function showMainView():void
{
logger.debug("showMainView");
this.view.removeChild(this.view.progressBar);
this.view.addChild(new MainView());
}
Now this may seem like overkill for this extremely simple example, as I could have just listened to the “serviceLocatorServicesLoadComplete” in the ApplicationViewMediator, but I wanted to proxy the event through the ApplicationController in case more needed to be done…What if we wanted to update a model level var to indicate that the SL was complete? What if we wanted to tell other objects that the SL was complete?…we wouldn’t want to do that from the ApplicationViewMediator as it’s sole purpose is to lend a hand to the MainView UI component and not to orchestrate application level processing, work, etc. Again, overkill in this example, but I wanted to put it in here as an example of what you might want to do in a much larger application.
Step 4: Inject the SL into the Delegates
Next we’ll want to inject the SL into the Delegates as opposed to injecting the hardcoded services defined in the BeanLoader. First, let’s declare the new serviceLocator property for the Delegate:
[Autowire(bean="serviceLocator")] public var serviceLocator:DynamicServiceLocator;
Next, let’s remove the service var’s declaration and create a simple getter method for it instead:
public function get service():HTTPService
{
return this.serviceLocator.getMXMLHTTPService("loginService");
}
Step 5: Request Service From SL
This allows us to leave the actual use of the service property untouched in our service method calls - this code hasn’t changed from Part 1, but I’m showing it for clarity. We’ll look at the Login Delegate for example:
public function login(dto:LoginDTO):AsyncToken
{
logger.debug("login");
return this.service.send();
}
Wrapping Up
The only other thing that I failed to mention that’s different from Part 1 is the use of a simple ProgressBar component in the Application root to provide the user with some additional feedback while the SL is loading and parsing. If you look at the main application file EmpMgmtConsoleSwizPassiveView you’ll notice that the MainView is now replaced by the ProgressBar component — once the SL is complete the ProgressBar is removed from the stage and the MainView is added (as mentioned in Step 3).
Finally, to test out the dynamic SL, open up the services.xml file in the example source code and comment out the first set of services that point to local, project-level XML files and ucomment the services that point to the WASI server. See, no recompiling.
And that about does it. Any questions?
Swiz Passive View Example :: Part 1
Introduction
If you followed my previous posts on the Employment Management Console application leveraging Spring ActionScript + Cairngorm, then you’ll be familiar with this small example as I simply ported it over from SAS + CG to Swiz using the Passive View (PV) design pattern.
Tutorial Goal: Create an Employee Management Console using Swiz and the Passive View Design Pattern
Assets
Acronyms
- PV = PassiveView
- VM = ViewMediator
- CG = Cairngorm
- IoC = Inversion of Control
- DTO = Data Transfer Object
Caveats
I won’t go into the details of what Swiz is and how it works as I’d like to let the code speak for itself and you can just hit up the Swiz homepage and view their simple examples; that said, I’ll point out several things I really like about Swiz.
I am using Swiz 0.6.4, as I found issues with the 1.0.0 alpha release (that I’m going to bring up with Ben Clinkenbeard).
As an additional side note, I chose the PV because I was always a fan of code-behind — I like separation of concerns so much that I actually detest looking at MXML with ActionScript in it — what a mess!..that and the fact that MXML is declarative and should just perform basic UI layout and not presentation logic. What I love about the PV is that it relies on composition as opposed to inheritance (ie the biggest issue I have with code-behind).
Finally, there are other examples of the PV + Swiz out there, like Ben Clinkenbeard’s example, but I went ahead and created an AbstractViewMediator that takes care of some race conditions when working with Views in a ViewMediator, like setting event handlers and data bindings. And I did some other things that I didn’t see in others that I’ll point out.
Swiz is Noninvasive
Swiz is a noninvasive framework that allows you to provide well organized structure and architecture to your app without writing a ton of boilerplate code that can be overly verbose and time consuming. Remember CG and how you were forced to do the following for service calls:
- Create an
Eventthat extends theCairngormEvent. - Create a
Commandto act as a responder for your service call. - Create a corresponding
Delegatethat actually makes the service call and transforms your data to client-side types before returning it to theCommand. - Don’t forget to map your Custom CG Event to your Command in the
FrontController. - Wash, rinse, repeat…and repeat…and repeat…and…mehhh
With Swiz, you can (and I emphasize can b/c you can still do it the old school way too) just broadcast a DynamicEvent from any object in the IoC Container (those objects that are defined in the Swiz BeanLoader — in my example, it’s in the class Beans.mxml) and then add some metadata to another object in the Container to “Mediate” that event.
Here’s an example — so in my LoginViewMediator I want to broadcast a “login” type event and pass the username and password along with it and I want my LoginController to handle this event:
private function submitBtnClickHandler(evt:MouseEvent):void
{
logger.debug("submitBtnClickHandler");
var dto:LoginDTO;
// create a login dto to contain the required fields for login
dto = new LoginDTO();
dto.userName = this.view.userNameTextInput.text;
dto.password = this.view.passwordTextInput.text;
var dynEvt:DynamicEvent = new DynamicEvent("login");
dynEvt.dto = dto;
this.eventDispatcher.dispatchEvent(dynEvt);
}
And now we handle this event in the LoginController like so:
[Mediate( event="login", properties="dto" )]
public function login( dto:LoginDTO ):void
{
logger.debug("login");
// TODO - make service call -- see in example src code
}
So let’s dig a bit deeper here and note all the cool stuff that’s going on and why I’m psyched about Swiz + PV:
No Custom Events (If You Don’t Want To)
Notice that I didn’t have to create a custom Event — less code — YES! Now, you can by all means create a custom event and Swiz will type check the event object and the event type to make sure they exist — check it out in the Swiz Docs for Event Handling — or you can save yourself some extra code and do what I did. While I’m usually a stickler for strongly typed objects, sometimes you have to ask yourself it it’s not just overkill for something as simple as this…personally preference I suppose, but less code without sacrificing organization and good practices wins that battle in my head.
Event Mediation
Swiz automatically handles the event and passes the meat of the event (the LoginDTO that I packages in my DynamicEvent) for me…uhm, yeah…that’s frigging cool! And you can pass multiple args as well. Again, see Swiz Docs. NOTE: One thing to remember about event mediation is that the handler method must be marked public — I’m used to making event handlers protected (and sometimes even private), but this won’t allow the Swiz event mediation to work it’s magic so make sure event handler methods are marked as public.
No Logic in MXML
My LoginViewMediator grabs the username and password from it’s corresponding LoginView and creates the event and DTO. The actual LoginView is just MXML…nuff said.
Event Dispatching and Handling Thereof in IoC Container Objects
Notice that the LoginViewMediator broadcasts the event from a class member variable called “eventDispatcher”. This next part is important: in order for Swiz to Mediate events, the events need to go through the central event bus using Swiz.dispatch(<eventType>) or by using the event dispatcher in the BeanLoader — I have chosen the latter. Why?…I have 2 reasons:
- I don’t like having a reference of Swiz in my classes — it’s just unnecessary and makes my application that much more coupled to the framework, something Swiz tries to get developers away from to begin with.
- The Swiz.as class is a singleton and singletons are bad…this is too long a topic to get into, but my main reason has to do with the use of Modules in large Flex apps and while this example doesn’t require modules, I’d still rather just stay the hell away from singletons.
I shove a reference of the BeanLoader’s eventDispatched into my IoC Container managed objects like this:
<controller:LoginController id="loginController" eventDispatcher="{this.dispatcher}" />
<mediator:LoginViewMediator id="loginViewMediator" eventDispatcher="{this.dispatcher}" />
Note that they both don’t need it in order for the controller to hear events from the view mediator — just the object broadcasting the event needs to have a reference to it, but I pushed it into the controller as well as since I’m broadcasting other events from it that I’d like other managed objects to listen to via mediation.
AbstractViewMediator
Since all of my views need a reference to their corresponding view, I decided to create a super class for all the mediators called AbstractViewMediator. This bad boy does several things starting with the injection of the view:
[Autowire( view="true" )]
public function set view( value:* ):void
{
logger.debug("AUTOWIRE :: view = " + value);
this._view = value;
// determine if the view has been initialized...
// NO...listen for it;s creation complete event
if(this._view.initialized == false)
{
this._view.addEventListener(FlexEvent.CREATION_COMPLETE, onCreationComplete);
}
// YES...call the init() method to kick off the instation of the view mediator
else
{
this.init();
}
// don't get in GC's way if the view is removed
this._view.addEventListener(Event.REMOVED_FROM_STAGE, cleanup);
}
Again, there’s a couple things to note about this method:
- The argument for the method is untyped, so how does the concrete
ViewMediator(VM) know how to inject the correct view? And then what about code-hinting for the view in the VM? Well, if you look at an actual implementation of a VM, like theLoginViewMediatoryou’ll see the following method:public function get view():LoginView { return this._view as LoginView; }Put this into method into each concrete VM with it’s corresponding view type and you’re good to go! Problem solved.
- We check to see if the view has been initialized, ie, has the creationComplete event fired and can I start to work with children components of this view without getting runtime errors. If it has, then we call the init() method (which can and should be overridden by concrete VMs to initialize themselves); if not, then we listen for the creationComplete event and call the
init()method then and only then. This ensures that when we want to set event handlers and data bindings on the view’s children we know they exist and we don’t hit nulls leading to runtime errors. - Finally, if you look at the
init()method in the AbstractViewMediator, you’ll see that it also calls thesetViewListeners()andsetViewDataBindings()methods — these are placeholder methods where developers can again add concrete event handlers and data bindings in VM subclasses by overriding them.
The last thing the AbstractViewMediator does is provide the event dispatcher class member variable that we discussed earlier: public var eventDispatcher:IEventDispatcher;
Controllers & Delegates
In Swiz, we eliminate the idea of a Command class (from Cairgorm) and kind of replace it with the controller — I say kind of b/c it’s not exact mapping as the Cairngorm Command follows the actual J2EE Command design pattern whereas Swiz uses a controller to handle events from the UI to make service calls, act as a responder for the service calls, and finally to orchestrate what happens in the app after the service call (usually by dispatching an event). Since we already looked at the event mediation of the in the LoginController we won’t discuss that, but we will dig into how the controller leverages a corresponding LoginDelegate to call the service and perform data transformations on the service’s response object. So let’s look at the internals of the actual login event mediation in the LoginController:
[Mediate( event="login", properties="dto" )]
public function login( dto:LoginDTO ):void
{
logger.debug("login");
var call:AsyncToken = this.delegate.login(dto);
// I created 2 ways to handle the login service delegate
// you can either have the result come back to the controller
// or you can catch the result in the delegate and have it perform
// the necessary data transformations (traditional approach) before
// kicking it back to this controller via an event
// APPROACH 1) have the results come back directly to this controller
//this.executeServiceCall(call, onLoginResult, onLoginFault);
// APPROACH 2) have the results come back the delegate for data transformations
// before coming back to this controller
this.executeServiceCall(call, this.delegate.result, onLoginFault);
}
Out of the box, Swiz recommends that the controller handle the actual service response and data transformations before deciding what the app should do next…that’s too much responsibility for one object in my opinion, so as you can see I decided to allow the LoginDelegate to handle the actual service response: this.executeServiceCall(call, this.delegate.result, onLoginFault);. After the delegate finishes with the response, it dispatches an event:
LoginDelegate
public function result(resultEvent:ResultEvent):void
{
logger.debug("result");
var userModel:UserModel;
var xml:XML;
var dynEvt:DynamicEvent;
// get the response typed as desired
userModel = this.getTypedResponse(resultEvent);
// let something know that the login delegate is done
dynEvt = new DynamicEvent("loginDelegateComplete");
dynEvt.userModel = userModel;
this.eventDispatcher.dispatchEvent(dynEvt);
}
And then the event is mediated by the LoginController where it updates the necessary model properties and dispatches an event to signify the end of the login process.
[Mediate( event="loginDelegateComplete", properties="userModel" )]
public function completeLogin(userModel:UserModel):void
{
logger.debug("completeLogin");
var role:String;
// populate the model with data from the response DTO
this.appModel.user = userModel;
// determine of user is an admin
for each ( role in userModel.rolesList )
{
if(role == RolesConstants.ROLE_ADMIN)
{
this.appModel.user.isAdmin = true;
break;
}
}
// set this last as this is what binds the view change
this.appModel.isUserAuthenticated = true;
var evt:DynamicEvent = new DynamicEvent(EVENT_LOGIN_COMPLETE);
this.eventDispatcher.dispatchEvent(evt);
}
I think that about covers it for this edition. Any questions?
More to come on Swiz in the future.
