Using Flex 2 Custom Events and Public Functions When Creating Custom Components
Introduction
A well-designed Flex 2.0 application involves integrating various components. Many of these components are provided for us in Flex (for example, DataGrid). But as you develop more sophisticated Flex applications you’ll want to create your own custom components.
Creating custom components that can be easily integrated into your overall Flex application but able to communicate with outside users involves two concepts – custom events and public functions. Custom events enable your Flex application to easily announce when something significant has occurred and for outside users to register and respond to the custom event. Public functions in your custom component provide an interface that allows users of your custom component to manipulate it.
I’ve created a simple Flex 2.0 application that you can view to demonstrate using custom events and public functions. Right click on the application to get the source code. My example involves two custom components, a ProductDisplay component that dispatches a custom event when the user clicks on a button to add a product to the shopping cart and a ShoppingCart component that keeps track of what products the user has added to his cart.
Custom Events
Many of the predefined Flex components come with their own set of events. For example mx:Button has a click event. You can consult the Flex 2.0.1 Language Reference to find what events a component may dispatch.
You can define an ActionScript function that will be called automatically when the click event is dispatched by the Button. When this event is dispatched, information about the event is included in the Event object that is received by the function designated to handle that event. For example, the Event object dispatched when you click on a Button object includes a target property, which can be used to find the information about the Button that generated the event.
When you create your own custom components, it’s a good design practice to have your components dispatch events that people using your component may want to be aware of and respond to. For example the ProductDisplay custom component dispatches an event when the user clicks on the Add to Cart button. That event will announce to anyone interested that the user wants to add a product to his shopping cart.
But how do you provide information about the product to the components listening for the “Add to Cart Event?” A product is not part of the standard properties of the Event object. To add additional “custom” properties that the Event object knows about you have to create your own custom event.
Creating a Custom Event
The “Creating A Subclass From The Event Class” reference below provides detailed instructions on how to create a custom event that includes adding additional properties to the Event object. The key is to declare the additional properties as public instance fields of your custom event class. You will also need to provide a constructor function that has a parameter for each of these additional properties and also override the clone function.
Below is the code for my custom event: AddToCartEvent. I’ve declared an instance field (property) of type Product. Product is just a simple ActionScript class used to store information about a product being displayed in the store (see Product.as under mystore package in the source code).
public class AddToCartEvent extends Event
{
/*declare our additional instance field
that this event can track */
public var product:Product;
//constructor
public function AddToCartEvent(product:Product, type:String) {
super(type);
this.product = product;
}//end constructor
//must override the inherited clone function
override public function clone():Event {
return new AddToCartEvent(product, type);
}//end clone
}//end class AddToCartEvent
When the user creates an AddToCartEvent object he must send to the constructor function a Product object and that Product object will be stored in the product instance field of the AddToCartEvent object. I can then refer to the product property whenever I have an AddToCartEvent by using event.product. Note that type is just a String that identifies the event type (for example "addToCartEvent").
Custom Component Dispatches the Custom Event
My ProductDisplay custom component includes a Button that the user can click on to signify that he wants to add that product to the shopping cart. Below is the function that is called when the user clicks on this button. In this function, I create an object of the AddToCartEvent custom event and pass to the AddToCartEvent’s constructor the product object the user wants to add to his cart.
private function addToCart(product:Product):void {
//create and dispatch the AddToCartEvent
var eventObj:AddToCartEvent = new AddToCartEvent(product, "addToCartEvent");
dispatchEvent( eventObj );
}//end addToCart
After you create the custom event object you have to dispatch it. Dispatching the event will cause any component’s that are “listening” for the event to call the function registered to handle that event. In the main Flex application where I’m using a ProductDisplay component to display each of my products, I have registered the function addToCartEventHandler to be called whenever the ProductDisplay component generates an AddToCartEvent.
<custom:ProductsDisplay product="{products.currentItem}"
addToCartEvent="addToCartEventHandler(event)" />
How does ProductDisplay notify outside users that it may dispatch the custom event “AddToCartEvent?” If you examine the beginning of the ProductDisplay custom component you’ll notice a mx:MetaData tag with an Event metadata keyword defined. Now the AddToCartEvent will be one of the visible attributes of my ProductDisplay component. Note that the type attribute include the complete path to the custom event class.
<mx:Metadata>
[Event(name="addToCartEvent",type="customevents.AddToCartEvent")]
</mx:Metadata>
Public ActionScript Functions
Another way to allow your custom components to interact with outside users is to include public ActionScript functions. These functions are designed so that users of your custom component may call the function and pass it any required data. If you’re not familiar with creating basic ActionScript functions consult the Basic Function Concepts reference below.
In my ShoppingCart custom component I’ve written a public addProductToCart function. A user of the ShoppingCart component can use this function to provide a Product object to the ShoppingCart component.
public function addProductToCart(product:Product):void {
//body of function omitted
}
Using the Public Function
In my main Flex application, examine the function addToCartEventHandler. This function is called in response to the ProductDisplay’s AddToCartEvent being dispatched. Inside the addToCartEventHandler I call the addProductToCart function of the ShoppingCart component (which I can refer to by its id value of cart). Since the custom Event object includes a product property I can pass the product the user wants to add to the cart to the addProductToCart function. Now the ShoppingCart component has received the product object and can do whatever it needs to with that product.
cart.addProductToCart( event.product );
Summary
As you study the source code for my example, note that the two custom components, ProductDisplay and ShoppingCart, are ignorant of each other and of the main Flex application. This means that I could change the ShoppingCart component or replace it with a much better ShoppingCart component and I would not have to change my ProductDisplay component. Also, I could use either of these components in other Flex applications.
Design your custom components so they can be easily integrated into your Flex application and reused in other applications by using custom events and public ActionScript functions. These techniques make your custom components flexible, responsive, and easier to change or replace.
References
- Basic Function Concepts, Programming ActionScript 3.0
- Classes in ActionScript 3.0, Programming ActionScript 3.0
- Creating Custom Events, Creating and Extending Flex 2 Components
- Creating a Subclass from the Event Class, Creating and Extending Flex 2 Components
- Event Class, Flex 2.0.1 Language Reference
This helps when setting a listener for the event as you can't accidently typo the name and wonder why it doesn't work for 3 hours :-)
The Flex framework code uses this convention.
http://livedocs.adobe.com/flex/201/html/createeven...
So my source code for the custom event AddToCartEvent should have this line:
public static const ADD_TO_CART_EVENT:String = "addToCartEvent";
Then when I create an object of the AddToCartEvent I can use this constant as follows:
var eventObj:AddToCartEvent =
new AddToCartEvent(product, AddToCartEvent.ADD_TO_CART_EVENT);
So instead of having two separate components for the two different functional requirements you would end up with one custom component that does both.
This would reduce our ability to reuse the component in other applications (a large custom component that does alot of different tasks isn't likely to be easily integrated into other applications). Combining the two components would make our application harder to maintain or change in the future.
Separating the functional requirements (display products, shopping cart) into two separate custom components makes it easier to change out components later and reuse our components in different applications (for example the ProductDisplay component could be use in our advertisement application).
***********
TypeError: Error #1009: Cannot access a property or method of a null object reference.
at com::DoSearchEvent()
at component::srch/doSearch()
at component::srch/___Button1_click()
*********************
*****Key areas of My Form ***********srch.mxml
<mx:Metadata>
[Event(name="doSearchEvent", type="com.DoSearchEvent")]
</mx:Metadata>
<mx:Script>
<![CDATA[
import com.DoSearchEvent;
import mx.controls.Alert;
public function doSearch():void {
Alert.show(keyword.text);
var myEventObj:DoSearchEvent = new DoSearchEvent("doSearchEvent",'keyword.text');
dispatchEvent(myEventObj);
}
]]>
</mx:Script>
<mx:Text text="Search:" />
<mx:TextInput id="keyword" width="150"/>
<mx:Button click="doSearch()" label="Go" />
**********************************
*******DoSearchEvent.as*********
package com
{
import flash.events.Event;
import mx.rpc.remoting.RemoteObject;
public class DoSearchEvent extends Event
{
public static const DO_SEARH_EVENT:String = "doSearchEvent";
public var keyword:String;
public var shopSearch:RemoteObject;
public function DoSearchEvent(type:String, keyword:String)
{
super(type);
shopSearch.getSearchData(keyword);
}
}
}
*************************
The alert shows the value of keyword.text, but I get the error. What am I doing wrong to this point? I can't seem to fix the problem.
---------------------------------
package com.cerebrum.utils {
import mx.rpc.events.ResultEvent
import mx.controls.Alert;
public class Result {
[Embed("/assets/images/dialog-information.png")]
public var IconDialogInformation:Class;
public function Result(event:ResultEvent,MyObject:Object):void {
Alert.show("teste", "teste", 4, null, null, IconDialogInformation);
}
}
}
---------------------------------
I as follows
import Result;
Result(event, meuobjeto);
---------------------------------
Is presented in the following error flex builder
1137: Incorrect number of arguments. Expected no more than 1.
---------------------------------
How should I inform the function that actually has 2 parameters?
<mx:Button id="btnFoo" label="Foo x 2" click="Foo(2)"/>
But not here?
btnFoo.addEventListener(MouseEvent.CLICK,Foo(2));
Now, it's obviously Adobe has coded the button component to allow for
arguments to be passed via the click parameter. But we're not given
that same privilege when using just AS3.
But obviously, they must have already coded this functionality. I'd
rather not have to re-invent the horse. Any answers?
you have swapped the parameters in the DoSearchEvent constructor, please use the data as the 1st parameter and the type as the 2nd parameter.
this will solve your problem.
The problem is that if I dispatch an event to get the data when it is returned if the two components have been instantiated then the data updates both instances (ie. the full list AND the second list that is filtered based on specific criteria).
What I would like is to be able to dispatch the request for data from the component and populate it only to the currently selected or active child. By that I mean the one the user is currently looking at.
Any ideas of how I can achieve this?