My favorites | Sign in
Project Logo
                
Search
for
Updated Sep 21, 2008 by Chris.Scott.One
DynamicMediators  
Automatic generation of DynamicMediators through the Mediate annotation

After working with Controllers for a while, you may begin to see a bit of a repeating pattern. It's common to have a simple view or dialog that edits an object's data, and when a user clicks submit, you send the object to a controller to save. Because you are a very conscientious programmer, always thinking of loose coupling and code reuse, you've designed the dialog to simply dispatch a save event that holds the object to save. However, somewhere in your application, you need to catch the save event and pass along the data to the save method in a controller. For a long time I have always set up an event listener on some owning view, which handles this 'mediating' between event and controller. Having the controller autowired into the view performing this mediation is very convenient, but the code is a little repetitive isn't it? And in the long run, I am just putting the coupling back into the 'parent' view. Certainly this can be alleviated by making my controllers a little smarter. I could register event listeners from within my controllers, and add local functions to mediate these events, which would delegate to other methods handing the event's data, but Swiz has an even simpler way, enter the DynamicMediator!

Swiz introduces a new 'Mediate' annotation that can automatically create event handlers for you, without writing a line of code. Let's take a look at our previous UserController.

public class UserController extends AbstractController
{

	public static const EVENT_SAVE_USER : String = "user:SaveUserEvent";
		
	[Autowire(bean="userService")]
	public var userService : RemoteObject;

	public function UserController() { }

	private function saveUser(user : User) : void {
		executeServiceCall(userService.save(user), saveUser_results);
	}

	// rest of class...
}

We'll assume we have a dialog that populates a User object to save and dispatches a SaveUserEvent.

private function saveUser() : void {		
	var event : SaveUserEvent = new SaveUserEvent(user);
	Swiz.dispatchEvent(user);
	close();
}

Now, instead of adding an event listener for SaveUserEvent, writing a function that retrieves the User from the event, and calling the saveUser function on our UserController, we will simply add the Mediate annotation directly to the UserController, above saveUser method we intend to invoke.

public class UserController extends AbstractController
{

	public static const EVENT_SAVE_USER : String = "user:SaveUserEvent";
		
	[Autowire(bean="userService")]
	public var userService : RemoteObject;

	public function UserController() { }
	
	[Mediate(event="user:SaveUserEvent", properties="user")]
	public function saveUser(user : User) : void {
		executeServiceCall(userService.save(user), saveUser_results);
	}

	// rest of class...
}

When Swiz loads this updated UserController, it sees the mediate annotation on the saveUser function and automatically constructs a DynamicMediator object, which will be added as an event listener for the event type defined in the annotation. The mediator will look for the 'properties' value of the annotation, and use them as arguments for the annotated function. Boilerplate code? Gone!

There are, of course a few simple rules to follow. The 'properties' value of Mediate lists the properties of your event in the order they should be passed into your function. So, if you have a function with the signature 'public function concatStrings(one : String, two : String)' which you would like be invoked when you dispatch a BigNameEvent, which has properties firstName, lastName, active, foo', you would add the annotation '[Mediate(event="eventBigName", properties="firstName,lastName")]. The dynamic responder will retrieve the firstName and lastName properties from the event, in the order listed in 'properties', and build an argument array used to invoke the concatString function. It's pretty simple, you just need to pay attention to the order you are defining your event properties to match your method arguments.

You also need to be aware of how you define the event type for the Mediate annotation. Annotations are not real code, they are custom medatada that the Flex compiler adds to an object or method's xml description. The flex compiler is able to evaluate variables and bindings in actionscript and mxml code, but not in medatata. This is why I am using a string value for the event type and properties. Because of this, I have taken to the convention of always defining my event types as public constants in my controllers. Even though at this time you cannot use these constants in the Mediate annotation, you can at least encapsulate the type values into your controller classes and provide compile time checking for all other uses of them in the rest of your code. Perhaps a future version of the Flex compiler will support dynamic evaluation of metadata, but for the mean time, this is a small price to pay for significant decoupling which DynamicMediators provide.

Adding the ability to have your Controllers automatically register interest in events is a huge timesaver, and has significantly reduced repetitive code in my own applications. Hopefully you will find as much benefit to the simple patterns offered and enjoy working with Swiz. Enjoy, and get Swizin!


Comment by vedovelli, Sep 24, 2008

Seems very nice and easy to learn and implement. You docs are great. I'll give it a try. Regards, Ved

Comment by HemTalreja, Oct 01, 2008

How do we remove a view that is mediated dynamically?

Comment by mraad23, Jan 15, 2009

How about doing something like the following in the DynamicMediator? class:

for each (var property : String in m_properties) {
if (event.hasOwnProperty(property)) {
args.push(eventproperty?)
} else {
throw new Error("Property '" + property + "' does not exist in event type '" + event.type);
}
}
Rather than using a try/catch ?

Comment by weiss.nate, Jan 21, 2009

Hi--

I wonder if it might make sense, given that the Mediate functionality sort of encourages the use of dynamic events (or at least removes some of the benefit of using non-dynamic events), if it might make sense for Swiz to include a convenience method called something like dispatchDynamicEvent.

This would just be to simplify things if you were dispatching events directly from MXML event handler attributes.

<mx:Buton

... click=" Swiz.dispatchDynamicEvent('user:loginEvent', {username:txtUsername.text, password:txtPassword.text}); "/>

Obviously this doesn't really solve any big problems, it would just be a bit of additional sugar for those who want to simplify all the event coding as much as possible (and are willing to live with the lack of compiler checking that strongly typed events provide).

Just a thought, not sure if it makes sense really. I may play around with it as a utility function for a while.

nate

Comment by dennis.croitoru, Jan 23, 2009

I support Nate! This dispatchDynamicEvent method makes perfect sense to me. I shall add this to my local version.

Comment by j...@saltwebsites.com, Jun 08, 2009

Nice. I'm rewriting part of a Cairngorm project with Swiz as a test and so far it's going very nicely.

In the doco on this page I think with

private function saveUser() : void {            
        var event : SaveUserEvent = new SaveUserEvent(user);
        Swiz.dispatchEvent(user);
        close();
}

You really mean

private function saveUser() : void {            
        var event : SaveUserEvent = new SaveUserEvent(user);
        Swiz.dispatchEvent(event);
        close();
}
Comment by vincent.spallek, Jun 22, 2009

I am trying to understand something here: Why do we dispatch a "save user" event, instead of simply auto-wiring the controller into the component that needs to save the user, and then calling saveUser() directly? The method saveUser() is obviously part of the interface definition of the controller, not part of the call back interface definition of the UI component. The UI component needs to know this part of the interface anyhow, so I don't see what we gain by decoupling it through events.

Comment by tom.chiverton, Sep 17, 2009

The bug for constants on metadata that would really help out here is https://bugs.adobe.com/jira/browse/SDK-21024, but hardly anyone has voted for it...

Comment by saverio.trioni, Nov 09 (2 days ago)

vincent: The point is that events allow to another part of the program to intercept the event, without the component even knowing it. The component could be reusable, or dumb, or anything, so it just sends out an event. Its container could implement any response to the event, or none at all.

If you wire the controller in the component, then it becomes a piece of the controller and you lose reusability at the very least!


Sign in to add a comment
Hosted by Google Code