Ext Event Handling

torstaina, tammikuuta 03, 2008

Just about all applications are event driven in one way or another. Click on a button in a Windows application and Windows will generate a message and dispatch it off to the main window handler and that will dispatch the message to another handler until something processes the event or returns it to the OS.

Web applications are event driven in a similar sort of way where the DOM defines events, like click, and event handlers. Ext extends the DOM event model through the Ext.util.Observable class. This class exposes three different ways of attaching event listeners and we'll go over all of them. We'll also talk about how some classes add shortcuts for defining specific kinds of events too.

There are three common ways to add a listener. The first is at the time of the object's creation through the listener configuration option. Its an array of event names with listener functions or listener configurations.

// Example 1: Simple Listener
function clickListener(){
alert ( 'Click listener called' );
}

new Ext.Button({
text: 'Click Me',
listeners:{
click: clickListener
}
}).render(document.body, 'example-1');

// Example 1a: Listener configuration
function clickListener2(){
alert ( 'Click listener2 called' );
}

new Ext.Button({
text: 'Click Me Too',
listeners:{
click: {
fn: clickListener2,
scope: this
}
}
}).render(document.body, 'example-1a');

Both examples bind an event listener to a button. The first example binds a listener function directly to an event. The second example binds a listener configuration to an event. Note the scope member lets you set the scope of the operation and that proves very handy for applications that use classes. You can bind event listeners after creation too. The addListener and on methods perform post creation listener binding.

// Example 2: Post creation event listeners
function clickListener3(){
alert ( 'Click listener3a called' );
}

function nextClickListener3(){
alert ( 'Click listener3b called' );
}

var button = new Ext.Button({
text: 'Click Me Now!'
});
button.addListener('click', clickListener3);
button.on('click', nextClickListener3);
button.render(document.body, 'example-2');

Something that I find interesting in this example is that you can chain event handlers. Both clickListener3 and nextClickListener3 will get called when the user clicks the button. This is pretty handy when you have several components working independently from each other but share a common starting point, like a button. Some objects have shortcuts for attaching event handlers. The button class has a configuration option for a handler. It also has a setHandler method for post-creation event binding.

// Example 3: handlers
function clickHandler(){
alert ( 'Click handler called' );
}

new Ext.Button({
text: 'Click Me w/handler',
handler: clickHandler
}).render(document.body, 'example-3');

The downside to using a shortcut is that you have to check the documentation to see which event activates your handler. I am inclined to use the verbose method (configuration, addListener or on) because it is always clear where a handler is bound. You can also create custom events and their handlers.


// Example 4: Custom Events
function firstClickHandler(){
alert ( 'firstClickHandler called' );
button2.fireEvent("customEvent");
}

function customEventHandler(){
alert ( 'customEventHandler called' );
}

var button2 = new Ext.Button({
text: 'Custom Event Button'
});

button2.addEvents( {"customEvent":true} );
button2.addListener('click', firstClickHandler);
button2.addListener('customEvent', customEventHandler);
button2.render(document.body, 'example-4');

We have two event handlers: the first to handle the click and the second to handle a custom even. We then fire the custom event when the click handler executes. The example shows how to create a new event type and how to fire it. A more real world example would be if you had several controls that needed to be updated when another control has changed as a result of a user. The originating component could be written to modify the values of the other controls, but this would require the originating control to understand the behavior of the dependent controls. This kind of code would couple the dependent control's behaviors with the originating control. It would be confusing and harder to maintain, especially if there were many dependent controls.

An alternative would be to expose a new event type in the originating control. Then add a fireEvent call to the originating control's event handler for a state change. Finally, have the dependent controls call the originating control's addListener (or on) method and register for the new event type. The dependent controls each implement an event handler for the new originating control's event. The resulting event handler is written to implement the specific behavior of the single dependent control. The benefit to this scheme is that many developers can work in a single UI and be dependent on the same originating UI control, yet not be stepping on each other since they are basically writing independently from one another.

// Example 5: Multiple dependent observer controls
// initialize the originating control
var button3 = new Ext.Button({
text: 'Custom Event Button'
});

button3.addEvents( {"stateChangeEvent":true} );

function originatingStateChangeEvent(){
alert ( 'originatingStateChangeEvent called' );
button3.fireEvent("stateChangeEvent");
}

button3.addListener('click', originatingStateChangeEvent);
// originating control code complete

// first dependent control
var dependentControl = /* some control type */
dependentControl.changeEventHandler =
function (){
alert ( 'changeEventHandler called' );
// code to update first dependentControl
};

button3.addListener('stateChangeEvent',
dependentControl.changeEventHandler,
dependentControl);

// second dependent control
var dependentControl2 = /* some control type */
dependentControl2.changeEventHandler =
function (){
alert ( 'changeEventHandler 2 called' );
// code to update first dependentControl2
};

button3.addListener('stateChangeEvent',
dependentControl2.changeEventHandler,
dependentControl2);

// etc
button3.render(document.body, 'example-5');

My final example shows how to pass additional parameters when firing an event. The fireEvent method is called with the event, a scope and any additional parameters I want to publish.

// Example 6: Custom Events
function firstClickHandlerParam(){
alert ( 'firstClickHandlerParam called' );
button4.fireEvent("customEvent", button4, 'some value');
}

function customEventHandlerParam(scope, p1){
alert ( 'customEventHandler called with: ' + p1 );
}

var button4 = new Ext.Button({
text: 'Custom Event Button with Param'
});

button4.addEvents( {"customEvent":true} );

button4.addListener('click', firstClickHandlerParam);
button4.addListener('customEvent', customEventHandlerParam);
button4.render(document.body, 'example-6');

Removing a listener is just as simple as adding one. You call either the un or removeListener method.

The event handling and dispatching system in Ext is easy for a developer to hook into. It doesn't take anytime to integrate with and its a breeze to extend. Happy coding!

You Might Also Like

0 comments