Posts Tagged metadata
Swiz EventHandler Filtering With Pub/Sub Topics - Part 2
After giving my first pass at this a little thought (which gives all of the details and explains how I got here) I realized the topic value defined in the EventHandler metadata tag was essentially hardcoded and set at compile time — this can work for some situations, but obviously isn’t ideal. I then decided I needed a way to filter events in Swiz by topics with data and values set at runtime. The event dispatching is still the same:
/**
* Allows this class to dispatch global events. Only methods using an
* EventHandler metatadata tag with the property of scope="global" can
* handle this event. This means other modules and even the shell application
* can handle this event if we want.
*
* <p>
* The event dispatcher is injected by Swiz due to the [Dispatcher] metadata
* and the class member's type of IEventDispatcher.
* </p>
*/
[Dispatcher(scope="global")]
public var globalDispatcher:IEventDispatcher;
...
public function sayHello():void
{
logger.debug("sayHello");
var appEvt:AppEvent;
appEvt = new AppEvent(AppEvent.SAY_HELLO);
appEvt.hello = "Hello World!";
appEvt.topic = "wasiTopic";
this.globalDispatcher.dispatchEvent(appEvt);
}
But the event handling is setup with a new, additional attribute called topicProperty.
public var myTopic:String = "wasiTopic";
...
[EventHandler(event="AppEvent.SAY_HELLO", properties="hello", scope="global", topicProperty="myTopic")]
public function helloWithTopic(hello:String):void
{
logger.debug("hello = " + hello); // WORKS
}
This now allows the developer to set the member variable for the object doing the event handling at runtime. Internally in Swiz’s org.swizframework.utils.event.EventHandler’s method public function handleEvent( event:Event ):void we now look for the attribute “topic” and “topicProperty” in the EventHandler metadata. If the user has set the topicProperty, the method looks at the Bean that’s using EventHandler and inspects it for this String property and then compares it to the topic passed in the event. This requires some more changes to the Swiz class org.swizframework.utils.event.EventHandler.
Grab the changes I made to Swiz here.
Swiz + SourceMate + Metadata Preferences FTW!
Posted by brianr in flash builder, swiz on April 22nd, 2010
I’ve obviously been using Swiz quite a bit lately and recently I added SourceMate to my arsenal of Flex tools — if you don’t know what SourceMate is, just think of what a good Java IDE can do and envision Flash Builder being able to do the same — it’s basically like feeding Flash Builder steroids or Big Papi in 2007 with PEDs. Anyways, here’s a quick list of goodies that it adds to the IDE.
Well my favorite set of enhancements is without a doubt the MetataData Features:
No more trying to remember the exact syntax for custom metadata for frameworks like Swiz…and even better is the fact that the Swiz doods at UM created a metadata config/preferences XML file for you to import to just start your Swiz project with SourceMate. Grab the file here from the Swiz Github repo and simply add it to your projects’s SourceMate preferences: Project Properties -> SourceMate -> Metadata -> Import… browse for the file and boom, you’r
e good to go!

SourceMate Metadata -> Import Swiz Configs
Nice work Swiz doods.
big-papi, dev-tools, development, dispatcher, flex, ide, inject, mediate, metadata, postconstruct, postdestroy, sourcemate, swiz
Swiz 1.0 RC1 Removes [Autowire( view="true" )] - Requires New AbstractViewMediator
Introduction
With the release of Swiz 1.0 RC1 right around the corner I figured I’d pull down the latest code from github, do a build, and then migrate my previous examples of Swiz and the Passive View to the latest SWC. I obviously ran into a couple of small changes between Swiz 0.6.4 and 1.0, but the one that caught me off-gaurd was the removal of metadata that injected views:
[Autowire( view="true" )]
Of course the first thing I tried was changing the metadata to use the new, preferred metadata tag [Inject]:
[Inject( view="true" )]
No dice. So I reached out to Ben Clinkinbeard and confirmed what I suspected — the injection of views (or any objects for that matter) that aren’t defined in the BeanLoader is no longer supported in Swiz 1.0; however, a simple change to my existing, base AbstractViewMediator and any concrete impls (subclasses thereof) fixed that right away — thanks to Ben for pointing me in the right direction on this one.
Assets:
Let’s just take a quick look at what the new AbstractViewMediator looks like:
AbstractViewMediator
/**
* Web App Solution Confidential Information
* Copyright 2010, Web App Solution, Inc.
*
* @date April, 15, 2010
*/
package com.webappsolution.mediator
{
import com.webappsolution.logging.LocalConnectionLog;
import com.webappsolution.model.IAppModel;
import flash.events.Event;
import flash.events.IEventDispatcher;
import mx.core.UIComponent;
import mx.events.FlexEvent;
import mx.logging.ILogger;
/**
* The base class for all view mediators. It's most useful function is to
* provide the metadata and setter method to inject the corresponding view into
* the mediator.
*
* <p>
* In addition, it allows concrete View Mediators to add view event handlers
* and data bindings to children of the corresponding View with confidence by
* by determining if the view is "alive" (aka has been instantiated and added to
* the stage -- creationComplete event fired) or if it needs to listen for it's
* creation complete event before performing actions on it. See the
* <code>public function setView( value:* ):void</code> method for more
* details on this.
* </p>
*/
public class AbstractViewMediator
{
/**
* Logger.
*/
private static const logger:ILogger = LocalConnectionLog.getLogger(AbstractViewMediator);
/**
* A flag indicating if the correpsonding view's creation complete has fired.
*/
public var isViewCreationComplete:Boolean = false;
/**
* The view class we're injecting into the view mediator.
*/
protected var viewType:Class;
/**
* The view that corresponds to this view mediator.
*/
protected var _view:UIComponent;
/**
* Constructor.
*/
public function AbstractViewMediator(viewType:Class)
{
super();
logger.debug("Constructor");
this.viewType = viewType;
}
/**
* Allows this class to dispatch events. The event dispatcher is
* injected by Swiz due to the [Dispatcher] metadata and the class
* memeber's type of IEventDispatcher.
*/
[Dispatcher]
public var dispatcher:IEventDispatcher;
/**
* Instruct Swiz to Inject the ApplicationModel by type.
*/
[Inject]
public function set appModel(value:IAppModel):void
{
logger.debug("AUTOWIRE :: appModel = " + value);
this._appModel = value;
}
public function get appModel():IAppModel
{
return this._appModel;
}
private var _appModel:IAppModel;
/**
* Listen for views added to the stage and determine if it's the corresponding
* View for this View Mediator. This check is done by looking at the view added
* to the stage and do a simple compare on the <code>viewType:Class</code> class
* member that we set in concrete View Medators via their constructors.
*
* <p>
* Also determine if the View has fired its creation complete event so developers
* can perform actions on the view from the View Mediator without hitting null pointers
* for uninstantiated objects in the View. If the creation complete has not fired, then
* listen for it.
* </p>
*/
[Mediate( "flash.events.Event.ADDED_TO_STAGE", properties="target", useCapture="true" )]
public function setView(value:*):void
{
if(value is viewType)
{
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.isViewCreationComplete = true;
this.init();
}
// don't get in GC's way if the view is removed
this._view.addEventListener(Event.REMOVED_FROM_STAGE, cleanup);
}
else
{
// logger.warn("Don't inject the view " + value);
}
}
/**
* The <code>init()</code> method is only called when the ViewMediator's corresponding
* View's creationComplete has been fired; this allows developers to add event listens,
* data bindings, and any other view functions in confidence (without worry about accessing
* children of the view that are null and cause runtime errors.)
*
* <p>
* Developers can override this to perform additional initializaitons in their ViewMediator,
* but must call <code>super.init()</code> in order for the aforementioned to function
* correctly.
* </p>
*
* <p>
* Does not have to be overriden. Simple here for convenience and as a marker method
* </p>
*/
protected function init():void
{
logger.debug("init");
this.setViewListeners();
this.setViewDataBindings();
}
/**
* Set the listeners for the UI components on the stage for the ViewMediator's corresponding View.
*
* <p>
* Does not have to be overriden. Simple here for convenience and as a marker method
* </p>
*/
protected function setViewListeners():void
{
logger.debug("setViewListeners");
// OVERRIDDEN
}
/**
* Set the data bindings for the UI components on the stage for the ViewMediator's corresponding View.
*
* <p>
* Does not have to be overriden. Simple here for convenience and as a marker method
* </p>
*/
protected function setViewDataBindings():void
{
logger.debug("setViewDataBindings");
// OVERRIDDEN
}
/**
* Listen for the creation complete of the View for this ViewMediator.
*/
protected function onCreationComplete( event:FlexEvent ):void
{
logger.debug("onCreationComplete");
this.isViewCreationComplete = true;
this._view.removeEventListener(FlexEvent.CREATION_COMPLETE, onCreationComplete);
this.init();
}
/**
* Remove any listeners we've created.
*
* <p>
* Developers should override this method to remove any custom listners
* created in the concrete ViewMediator. Developers must call
* <code>super.cleanup()</code> in order for this to function correctly.
* </p>
*/
protected function cleanup( event:Event ):void
{
logger.debug("cleanup");
this._view.removeEventListener(Event.REMOVED_FROM_STAGE, cleanup);
}
}
}
The key change here is the changes to the public function setView(value:*):void method. Notice how it’s mediating the added to stage event — this event mediation checks to see if the View that was added to the stage is in fact the corresponding View for this View Mediator. Now you have to ask, but where is the viewType set for this check? And the answer is in each concrete impl’s constructor of the View Mediator. Here’s a quick example in a LoginViewMediator:
LoginViewMediator
/**
* Web App Solution Confidential Information
* Copyright 2010, Web App Solution, Inc.
*
* @date April, 15, 2010
*/
package com.webappsolution.mediator
{
import com.webappsolution.event.ApplicationEvent;
import com.webappsolution.logging.LocalConnectionLog;
import com.webappsolution.view.LoginView;
import flash.events.Event;
import flash.events.MouseEvent;
import mx.controls.TextInput;
import mx.events.ValidationResultEvent;
import mx.logging.ILogger;
import mx.validators.EmailValidator;
import mx.validators.StringValidator;
import mx.validators.Validator;
/**
* Handles all events, data manipulation, data bindings, and whatever else it's
* corresponding View can dish out.
*/
public class LoginViewMediator extends AbstractViewMediator
{
/**
* Logger.
*/
private static const logger:ILogger = LocalConnectionLog.getLogger(LoginViewMediator);
/**
* Field validator for the user name form field.
*/
protected var userNameValidator:EmailValidator;
/**
* Field validator for the password form field.
*/
protected var passwordValidator:StringValidator;
/**
* Constructor.
*/
public function LoginViewMediator(viewType:Class=null)
{
super(LoginView);
logger.debug("Constructor");
}
/**
* Since the AbstractViewMediator sets the view via Autowiring in Swiz,
* we need to create a local getter to access the underlying, expected view
* class type.
*/
public function get view():LoginView
{
return this._view as LoginView;
}
/**
* The <code>init()</code> method is fired off automatically by the
* AbstractViewMediator when the creation complete event fires for the
* corresponding ViewMediator's view. This allows us to listen for events
* and set data bindings on the view with the confidence that our view
* and all of it's child views have been created and live on the stage.
*/
override protected function init():void
{
super.init();
logger.debug("init");
// create the form validators for the login panel
this.createFormValidators();
// disable the submit btn to start
this.view.submitBtn.enabled = false;
// make the submit btn the default btn so hitting enters submits the form
this.view.loginForm.defaultButton = this.view.submitBtn;
// set the focus to the username field
this.view.userNameTextInput.setFocus();
}
/**
* Create listeners for all of the view's children that dispatch events
* that we want to handle in this mediator.
*/
override protected function setViewListeners():void
{
super.setViewListeners();
logger.debug("setViewListeners");
// handle user input on the form fields for instant form validation feedback
this.view.userNameTextInput.addEventListener(Event.CHANGE, inputChgHandler);
this.view.passwordTextInput.addEventListener(Event.CHANGE, inputChgHandler);
// handle the click of the submit button
this.view.submitBtn.addEventListener(MouseEvent.CLICK, submitBtnClickHandler);
}
/**
* Handles the click event from the submit button.
* Grabs the username and password from the respeective text
* input fields and populates a LoginDTO, then dispatches the
* LoginEvent to the Cairngorm framework.
*/
protected function submitBtnClickHandler(evt:MouseEvent):void
{
logger.debug("submitBtnClickHandler");
var userName:String;
var password:String;
userName = this.view.userNameTextInput.text;
password = this.view.passwordTextInput.text;
this.login(userName, password);
}
/**
* The functional login method that dispatches the login event with the
* username and password.
*
* @param userName
* @param password
*/
protected function login(userName:String, password:String):void
{
var appEvt:ApplicationEvent;
appEvt = new ApplicationEvent(ApplicationEvent.SERVICE_LOGIN);
appEvt.username = userName;
appEvt.password = password;
this.dispatcher.dispatchEvent(appEvt);
}
/**
* Create the form field validators for the login fields.
*/
protected function createFormValidators():void
{
logger.debug("createFormValidators");
// create the user name validator
this.userNameValidator = new EmailValidator();
this.userNameValidator.requiredFieldError = "Please enter a valid email address.";
this.userNameValidator.property = "text";
// create the password validator
this.passwordValidator = new StringValidator();
this.passwordValidator.required = true;
this.passwordValidator.requiredFieldError = "Please enter a password.";
this.passwordValidator.minLength = 4;
this.passwordValidator.property = "text";
}
/**
* Handles text input changes to the username and password and validate them.
* Only enable the submit button if both the username and password fields
* are valid.
*
* @param evt Change event from the targeted input field.
*/
protected function inputChgHandler(evt:Event):void
{
this.view.submitBtn.enabled =
this.isTextInputFieldValid(this.view.userNameTextInput, this.userNameValidator) &&
this.isTextInputFieldValid(this.view.passwordTextInput, this.passwordValidator);
}
/**
* Determines if the username text input is valid.
* @return Boolean
*/
protected function isTextInputFieldValid(textInput:TextInput, validator:Validator):Boolean
{
textInput.errorString = "";
var resultEvent:ValidationResultEvent = validator.validate(textInput.text);
if (resultEvent.type != ValidationResultEvent.VALID)
{
textInput.errorString = resultEvent.message;
}
return (resultEvent.type == ValidationResultEvent.VALID);
}
/**
* Remove any listeners we've created.
*/
override protected function cleanup( event:Event ):void
{
super.cleanup(event);
logger.debug("cleanup");
this.view.userNameTextInput.removeEventListener(Event.CHANGE, inputChgHandler);
this.view.passwordTextInput.removeEventListener(Event.CHANGE, inputChgHandler);
this.view.submitBtn.removeEventListener(MouseEvent.CLICK, submitBtnClickHandler);
}
}
}
There are two things to note here:
1) The setting of the view type via the Constructor
public function LoginViewMediator(viewType:Class=null)
{
super(LoginView);
logger.debug("Constructor");
}
2) The use of a getter method for strong typing of the View in the View Mediator
public function get view():LoginView
{
return this._view as LoginView;
}
Now armed with a new version of the AbstractViewMediator you can go back to using Swiz with your View Mediators without any issues.
NOTE: In order for this to work you must pull down the latest code from github and compile the SWC with the ant build.xml file provided with the project. Do not attempt to use this with the currently released Swiz Beta 1 SWC.
autowire-view, dependency-injection, form-validation, inject, login-view, metadata, passive-view, swiz-1.0, view-mediator
-
-
Archives
- April 2011 (2)
- March 2011 (1)
- February 2011 (1)
- January 2011 (1)
- December 2010 (5)
- October 2010 (4)
- September 2010 (1)
- August 2010 (2)
- April 2010 (6)
- March 2010 (2)
- January 2010 (2)
- December 2009 (1)
- November 2009 (7)
- October 2009 (3)
- September 2009 (1)
- August 2009 (3)
- July 2009 (6)
- June 2009 (5)
- May 2009 (9)
- April 2009 (8)
- March 2009 (2)
- January 2007 (1)
- August 2006 (1)
-
Meta