Handling events in the view

0 views
Skip to first unread message

Freddie

unread,
Feb 5, 2009, 5:00:30 PM2/5/09
to OpenFlux
it is possible to automatically wire up events in the view to handlers
in the controller by using the [ViewHandler] metadata tag. Using this
tag wires up a function in the controller to handle a particular event
from the view itself. Therefore the event.currentTarget in the handler
will always be component.view. Ben suggested in the now closed
discussion
http://groups.google.com/group/openflux/browse_thread/thread/46bc71ad0377bc15/5439efa0cd872749?lnk=gst&q=target#5439efa0cd872749
that if you were interested in a click event on a particular child of
view, rather than the view as a whole, you could look at
event.target.

However looking at the event.target doesn't always work, because if
the
sub-control that you're interested in has subcomponents, one of those
might be the event.target. Therefore need to check if the control is
either the event.target, or one of it's direct ancestors. I've written
a class which achieves this:


package at.newbe.view.utils
{
import flash.display.DisplayObject;
import flash.events.MouseEvent;


/**
* ...
* @author Frederick G Fisher IV
* @copy Newbe.at
*/
public class ViewHandlerUtil
{

public static function isDescendant
(target:DisplayObject,
ancestor:DisplayObject, context:DisplayObject=null):Boolean {

//what's the top level element in the search?
var root:DisplayObject = (context) ? context :
target.root;
if (!root) return false

//search up the displayobject hierarchy
var current:DisplayObject = target;
while (current) {
if (current == ancestor) return true;
else if (current == root) break;
current = current.parent;
}
return false;
}

public static function executeHandler(event:MouseEvent,
handlers:Array, context:DisplayObject=null,
stopProp:Boolean=true):void {
var target:DisplayObject = event.target as
DisplayObject;

//go through the instructions of what handler
for what ancestor, do
the appropriate handler
for each (var handler:Array in handlers) {
var ancestor:DisplayObject = handler[0]
as DisplayObject;
if (isDescendant(target, ancestor,
context)) {
var handlerFunction:Function =
handler[1] as Function;
handlerFunction(event);
if (stopProp)
event.stopPropagation();
break;
}
}
}
}

}


you use it like this:

[ViewHandler(event="mouseUp", handler="mouseUpHandler")] //at top of
class

[ViewContract] public var control:ControlType;


metadata function mouseUpHandler(event:MouseEvent):void {
ViewHandlerUtil.executeHandler(event,
[[control, controlHandler]],
component.view as DisplayObject);
}

private function controlHandler(e:MouseEvent) { trace("clicked the
control"); }


That is to say:

1. metadata a ViewHandler for whatever kind of mouse event you're
interested in
2. make a view contract variable that will either reference the
control you want directly, or type component.view to a custom view
that has controls of same type but different ids
3. in the ViewHandler function, call ViewHandlerUtil.executeHandler
(event, [control/handler mappings], typically component.view as
DisplayObject)
4. implement the handlers from the control/handler mappings array


This is what it might look like with a custom view with buttons id of
button1, button2, button3...
(n.b please correct if you can't use ViewContract to reference the
view itself, if not then reference component.view as CustomViewType in
the mouseUpHandler)

[ViewContract] public var customView:CustomViewType;


metadata function mouseUpHandler(event:MouseEvent):void {
ViewHandlerUtil.executeHandler(event,
[[customView.button1,
button1Handler],[customView.button2, button2Handler],
[customView.button3, button3Handler]], component.view as
DisplayObject);
}

private function button1Handler(e:MouseEvent) { trace("clicked
button1"); }
private function button2Handler(e:MouseEvent) { trace("clicked
button2"); }
private function button3Handler(e:MouseEvent) { trace("clicked
button3"); }


This works in a kind of roundabout way compared to the direct way, of
adding an id field to the ViewHandler directive, but it works directly
without changing anything already in OpenFlux so you can use it today.
I'm interested in the logic of not doing haivng id in the ViewHandler
directive, perhaps to force events to be dealt with 'centrally' in the
controller? or to make it awkward and encourage controls to deal with
their own events?

by the way, any help from anyone on my issues with lists and setting
default components sizes would be much appreciated :-D

peace and love xx

Ryan Campbell

unread,
Feb 6, 2009, 11:50:04 AM2/6/09
to open...@googlegroups.com
It's actually way easier then you think if you use [EventHandler] and [ViewContract]. Look at ScrollBarController for a great reference.

Here's a quick example:

public class TestController {
   [EventHandler(event="click", handler="myButtonClickHandler")]
   [ViewContract] public var myButton;

  metadata function myButtonClickHandler(event:MouseEvent):void
  {
    // event.currentTarget will be a reference to <mx:Button id="myButton"/> from your view

Freddie

unread,
Feb 15, 2009, 4:08:01 PM2/15/09
to OpenFlux
gosh yes that is a lot easier! How does this work? I thought that
[EventHandler] was for events from the model, not the view... in fact
it's called model handler in MetaUtil, and registers to events
dispatched by component. So is it working magically together with
ViewContract somehow? Or does ViewContract make a variable also in the
component? There seems to be quite a bit of serendipity going on here,
I'm guessing the name of the handler and the variable need to match,
how does that work behind the scenes?

I'm writing a blog post about the OpenFlux metadata at the moment,
it'd be helpful if you could clear this up for me.

On Feb 6, 4:50 pm, Ryan Campbell <r...@safaricreative.com> wrote:
> It's actually way easier then you think if you use [EventHandler] and
> [ViewContract]. Look at ScrollBarController for a great reference.
> Here's a quick example:
>
> public class TestController {
>    [EventHandler(event="click", handler="myButtonClickHandler")]
>    [ViewContract] public var myButton;
>
>   metadata function myButtonClickHandler(event:MouseEvent):void
>   {
>     // event.currentTarget will be a reference to <mx:Button id="myButton"/>
> from your view
>   }
>
> }
>
> On Thu, Feb 5, 2009 at 4:00 PM, Freddie <freddiefis...@googlemail.com>wrote:
>
>
>
> > it is possible to automatically wire up events in the view to handlers
> > in the controller by using the [ViewHandler] metadata tag. Using this
> > tag wires up a function in the controller to handle a particular event
> > from the view itself. Therefore the event.currentTarget in the handler
> > will always be component.view. Ben suggested in the now closed
> > discussion
>
> >http://groups.google.com/group/openflux/browse_thread/thread/46bc71ad...

Ben Stucki

unread,
Feb 15, 2009, 4:51:51 PM2/15/09
to open...@googlegroups.com
Hey Freddie,

The magic for the EventHandler and ViewHandler metadata is in com.openflux.core.FluxController, which is the base class for most controllers. When we read the metadata we're able to tell if it was declared on the class (in which case we listen on the component) or if it was declared on a variable (in which case we listen on whatever gets assigned to that var). We should look at how we can name these to be a little more clear probably. thoughts? Also, the event handler functions can be called anything, it just needs to be described in the metadata. The ViewContract and EventHandler stuff doesn't really know anything about each-other. The ViewContract makes sure that a variable matches the same variable name on the view if applicable and the EventHandler attaches event listeners to whatever the variable is no matter how it gets there. Combined, they should be keeping everything in sync when views/vars change and update. Let me know if you have any more questions. I'm looking forward to that blog post. :-)

Freddie

unread,
Feb 16, 2009, 7:45:13 PM2/16/09
to OpenFlux
how very interesting. I'm wondering, how about create a new metadata
tag called [ModelHandler] which is used like [EventHandler] at the top
of a class, and limit [EventHandler] to preceding vars/accessors. I
think this would be more intuitive than having a single tag that can
work in two rather different ways.

On Feb 15, 9:51 pm, Ben Stucki <benstu...@gmail.com> wrote:
> Hey Freddie,
> The magic for the EventHandler and ViewHandler metadata is in
> com.openflux.core.FluxController, which is the base class for most
> controllers. When we read the metadata we're able to tell if it was declared
> on the class (in which case we listen on the component) or if it was
> declared on a variable (in which case we listen on whatever gets assigned to
> that var). We should look at how we can name these to be a little more clear
> probably. thoughts? Also, the event handler functions can be called
> anything, it just needs to be described in the metadata. The ViewContract
> and EventHandler stuff doesn't really know anything about each-other. The
> ViewContract makes sure that a variable matches the same variable name on
> the view if applicable and the EventHandler attaches event listeners to
> whatever the variable is no matter how it gets there. Combined, they should
> be keeping everything in sync when views/vars change and update. Let me know
> if you have any more questions. I'm looking forward to that blog post. :-)
>
Reply all
Reply to author
Forward
0 new messages