nwebb

Flex, Flash, AIR

Tip: Override Event.clone when redispatching event objects

Here's a quick tip I read about a long time ago, and it just came in handy (probably saved me some head-scratching) so I thought I'd pass it on.

Event.clone

If you've ever looked at the documentation for flash.events.Event you may have seen that the class has a method called clone which you can override.

The docs tell you that clone "duplicates an instance of an Event subclass". You'll rarely if ever want to call clone yourself. The EventDispatcher class calls it automatically for you when you redispatch an event. Let's look at an example of where this info comes in handy:

Example - Refactoring a View

Today I was moving some code out of a view and in to a presentation model. However, everything is set up to listen for a particular event being dispatched from the views. I didn't want to change the architecture and I didn't want to make the existing Event-subclass bubble either, so instead, I decided to dispatch the event from the model, listen for it in the view, and then re-dispatch it from the view.

The code in my model looked something like this:

Actionscript:
  1. //IN PRESENTATION MODEL CLASS
  2. var ev:StepEvent = new StepEvent(StepEvent.STEPNEXT); //create event
  3. var stepCondition:StepCondition = new StepCondition(args); //this event passes along a condition object, so create...
  4. ev.EventDataObject = stepCondition; //set the above object as a property on the custom event class
  5. dispatchEvent(ev); //groovy chicken

All the view needed to do was catch this event and pass it on up the chain (as if it has dispatched it itself):

Actionscript:
  1. //IN VIEW CLASS
  2. _helper.addEventListener(StepEvent.STEPNEXT, onStepNext, false, 0, true);
  3. ...
  4. ...
  5. private function onStepNext(event:StepEvent):void
  6. {
  7.     dispatchEvent(event); //take event and pass it on
  8. }

However if I don't override the clone method in StepEvent, I'll receive a runtime error which will look something like this:

TypeError: Error #1034: Type Coercion failed: cannot convert flash.events::Event@abd16a1 to Tools.Process.StepEvent.

I get the error because EventDispatcher is automatcially calling clone, but as I have not overridden it in my subclass, the code in the Event superclass will execute; In the view the onStepNext method is expecting an event of type StepEvent but receives an event of type Event instead. This down-cast fails and an error is thrown.

The solution is to override the clone method, and ensure that the event being returned contains all the info which is specific to my custom event:

Actionscript:
  1. //IN STEPEVENT CLASS
  2. override public function clone():Event
  3. {
  4.     var ev:StepEvent = new StepEvent(STEPNEXT);
  5.     ev.EventDataObject = this.EventDataObject;//EventDataObject is the public property on my custom event which stores the condition object.
  6.     return ev;
  7. }

Now everything should work as expected.
Hope this helps.

4 comments

The Presentation Model - post #2

In my last post I offered up a very simple example of the Presentation Model. Below is a simple, but slightly more complex example containing three NumericStepper components. It will hopefully highlight some of the subtle decisions you need to make when implementing the PM. The NumericStepper values must total 100; Their backgrounds go green when they do / red when they don't. Right-click the example below and choose "View Source" to see the code:

Get Adobe Flash player

Same Thing, More Code?

I wanted to create this functionality because it's almost identical to what I did in this PieChart example, except now we have more code (two additional classes in fact because I'm using a superclass). I'll leave it up to you to decide whether the additional code needed to implement the Presentation Model is worth it, but bear in mind the distortion given out by very simple examples such as this - it's when projects become larger that good architecture pays dividends. For something this small it is obviously massive overkill.

Again, this is not intended to be a "this is how it's done" post. It's just another file I created while getting to grips with the implementation. I think I've done things optimally and I thought it would be useful to share with others.  Feedback is always appreciated as usual.

What To Look For

So what are the main things to note here? First, the logic which totals up the values of the input boxes is in the model. Yet the logic which determines the colour of the backgrounds is in the view. I'm not saying this is correct, but it's a good example of the decisions you have to make about where to put things. At the time, I reasoned that the colour was a very view-specific thing and so it should go in the view rather than clutter up the model; but then again, the view is supposed to be a 'reflection' of the model, and it is suggested that properties such as "enabled" belong in the presentation model, so "inputStyle" may also belong in the model (your thoughts on this?). Although the two classes are not tightly coupled, I guess they are still very closely related. However I'll leave the example as is, because it shows how I hooked up the view to respond to changes in the model.

You'll notice that now, all the NumericSteppers get their values from the model, and that when their values are changed, they call setters in the model - the logic is determined by the model.

The presentation model inherits from a baseclass. I just took this from David's post - yes even the comments ( :) ), which in turn let you know that it was borrowed from Borre Wessel & Paul Williams' example, which incidentally I couldn't find.

The base class is stright forward enough, and the subclass really just declares a bunch of getter/setters, a handleFirstShow method and a setTotal method.  You can see that in handleFirstShow we call setTotal. The view has already used dataBinding to  set the values of the NumericSteppers; then setTotal totals up the value of aaa + bbb + ccc, before dispatching an event. This triggers the binding on the Label (which displays the total) and it also triggers the observe tag:

Actionscript:
  1. <ac:Observe
  2. source="{_pm.total}"
  3. handler="{onTotalValueChange}"/>

The observe tag is a shorthand way of saying: when _pm.total changes, trigger onTotalValueChange. You can achieve the same thing using ChangeWatcher, but this is nice and clean, and is taken from Paul Willliams' blog.
As you can see, in this example I've also got custom event names for the getter/setters. These are not essential - it's just that by default, binding always dispatches a PropertyChange event which means that all bindings in my view would be listening for that eventType ... so whenever any property changed, all the bindings in the view would sit up and say "Hey, is that event for me?". By decalring custom eventTypes (which requires you to manually dispatch the event), you should stop this from happening. Again, not at all essential here, but it's not a bad habit to get in to and I have a snippet which generates my getter/setter code for me anyway, so it wasn't any additional work.

I hope this example proves useful for anyone else taking a first or second look at the Presentation Model in Flex.

2 comments

The Presentation Model - post #1

I read Paul Williams' post about using the Presentation Model some time ago, but ran in to a couple of issues when trying to put it in to practise in a real-world project. Although I still use PMs, they are sometimes really helpers that are not quite following all the rules. David Deraedt also has a series of posts about using the Presentation Model and mentions issues he encountered (specifically using it alongside Cairngorm).

I won't go in to details about why I want to separate my code this way (the above links cover all of that), but I thought I'd take a fresh look and so I've gone back to basics by creating a really simple app based not on the demo app, but rather on a single bit of functionality from Martin Fowler's example here:

http://www.martinfowler.com/eaaDev/PresentationModel.html

All the app does is set the enabled status of the TextInput, based on the state of the CheckBox.
(Note: I've not bothered creating the Album DTO or storing the composer string, as they weren't strictly necessary and I wanted to keep the file as basic as possible. For the same reason I'm not dispatching custom events either, so for this example every binding will dispatch a PropertyChanged event.)

Hopefully my interpretation is correct - nearly all the comments in the code are copied/pasted directly from the Martin Fowler article so you can follow along. I've also set the initial value of the checkbox to true and used binding to provide the 'updating of the view' from the model.

If you've not come across the Presentation Model before I would suggest starting with this example by looking at the CheckBox in View.mxml, and then following through the process of what happens when a user clicks the CheckBox (this way the comments should be in some order).

Here is the example. Right-click for source code:

Get Adobe Flash player

Presentation Model & 'Global' Variables

I just wanted to air one additional thought here, as I'm working on a modular project which doesn't use an established architectural framework (i.e. Cairngorm, Mate, PureMVC) but has been set up in such a way that common/shared objects are retrieved using something similar to Cairngorm's ModelLocator. Essentially any module can get hold of shared data using sytax similar to:

Actionscript:
  1. Application.application.getSharedThingy([identifier]);

Now I don't want to get in to a discussion about Singletons or global vars here - as a contractor working on someone elses project, some projects are architected to depend on globals and there isn't always the time or budget for a refactor - I think this is just a reality of the job. However, one great thing about Flex is that general views can be knocked up in MXML in minutes (anything you want to reuse is a candidate for a component or helper class). Therefore, as far as code-reuse goes for Flex views goes, I'm really not that bothered about having a reusable view.

The great thing about the Presentation Model is that it forces us to remove all non-view logic in to its own file. The resulting PM should be encapsulated, easy to unit test and have no knowledge of the view (the view does have knowledge of the PM). If I start sticking references to globals/Singletons in the PM, I break some of those rules. However, if I see my View as something disposable, why not access the singleton/global objects from within the view, and then 'inject' them in to the PM?

So for example, my PM could have a setApplicationPayload() method, and my view would get the payload object and set it on the model:

Actionscript:
  1. pMod.setApplicationPayload(Application.application.getDataObject([identifier]));

I'm not saying this is the way to go, but it does make working with globals a little better - it was just a thought and I'd be interested to hear your feedback on things you do to make using the Presentation Model setup easier.

1 comment

Next Page »

Bad Behavior has blocked 639 access attempts in the last 7 days.