OK I am having some troubles with FLEX events, so being me I decide to write a little project to start to understand them on a smaller scale before throwing them into our huge application. But I wanted to mimic the same “structure” that we have in the application.
Here is the current Structure
So I built the play application similar to this one. I basically wanted to have the Map be notified when the state drop down was changed in any of the Search Types.
I created each of the items above as components with the Search Type having a combo box in it with values. We will call this the SearchTypeComponent.
<?xml version="1.0" encoding="utf-8"?><mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" width="100%" height="100%" ><mx:Script><![CDATA[import mx.collections.ArrayCollection;import events.SearchStateChangeEvent;[Bindable]public var States:ArrayCollection = new ArrayCollection([ {label:"Alaska", data:"AL"},{label:"Arizona", data:"AZ"},{label:"Colorado", data:"CO"} ]);]]></mx:Script><mx:ComboBox id="stateCombo" x="45" y="64"prompt="Please select a State..."dataProvider="{States}"/><mx:Text x="45" y="130" text="" id="textFld"/></mx:Canvas>
Not wanting to complicate the application with all of the map pieces, I created it as a component with a couple of text fields in it. For sake of simplification we will call this the DetailResultsComponent
<?xml version="1.0" encoding="utf-8"?><mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" width="400" height="300" ><mx:Text x="57" y="13" text="Text" width="98" id="state"/><mx:Text x="57" y="65" text="Text" width="98" id="city"/><mx:Label x="10" y="13" text="State:"/><mx:Label x="10" y="65" text="City:"/></mx:Canvas>
Now for the actual event I built an event class similar to the one I would need in my application
import flash.events.Event;public class SearchStateChangeEvent extends Event{public static const SEARCH_STATE_CHANGE_EVENT:String = "searchStateChangeEvent";public var stateCd:String;public var stateName:String;public function SearchStateChangeEvent(stateCd:String, stateName:String){super(SEARCH_STATE_CHANGE_EVENT);this.stateCd = stateCd;this.stateName = stateName;}}
Overview of what I did about. I created a basic event class that extends flash.events.Event. Let’s go over the basics of the SearchStateChangeEvent class. I created a public static const because you will need this when you add an event listener, which we will go over a bit later.
The constructor for the an event looks like this public function eventName(type:String, bubbles:Boolean=false, cancelable:Boolean=false). However, mine looks different than that. The reason is that because cancelable and bubbles have a default I do not need to pass them in. I do however; need to pass in a couple of parameters so they are in the constructor. I am calling super passing in the event name and since the other items (bubbles, and cancelable) default to false I am not passing them in.
The basic setup has been completed; from here I will go back and edit the two components. First the pitcher component, we will need to update to fire off the SearchStateChangeEvent event. The first thing we want to do is add a change property to the ComboBox.
<mx:ComboBox id="stateCombo" x="45" y="64"prompt="Please select a State..."dataProvider="{States}"change="onChange(event)" />
The change will call onChange method passing it the event. So now we will want to create a function to intercept the event.
private function onChange(event:Event):void{var searchEvent:SearchStateChangeEvent = new SearchStateChangeEvent(ComboBox(event.target).selectedItem.label,ComboBox(event.target).selectedItem.data);dispatchEvent(searchEvent);}
The method above will create an instance of the SearchStateChangeEvent and pass in the Label and Data from the combo box to the constructor. This conforms to the constructor we created, normally we would also have the bubbles and cancelable Booleans; but as we discussed earlier we are not using them. We then will need to dispatch the event by calling the component’s dispatchEvent. According to Adobe LiveDocs all components that extend the UIComponent have this method.
The syntax for the dispatchEvent() method is as follows:
objectInstancedispatchEvent(event:Event):Boolean
Now for the DetailResultsComponent we will need to update this to “catch” the event and perform any action that we want based on this event. First we will need to set the creationComplete on the component. I have put in creationComplete=”init()” this will call the init function.
private function init():void{addEventListener(SearchStateChangeEvent.SEARCH_STATE_CHANGE_EVENT, handleEvent);}
Here we use the addEventListener the function signature is
addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int = 0, useWeakReference:Boolean = false):void
When we create it we will pass in the event name, I use the static constant that was declared in the SearchStateChangeEvent earlier and a function name that will be called when this event is fired off.
private function handleEvent(event:SearchStateChangeEvent):void{state.text = event.stateCd;city.text = event.stateName;}
I called this function handleEvent and the parameter is the event we created earlier SearchStateChangeEvent. This is a simple method that updates the two text fields on the screen with the values we placed inside the Event earlier.
After running this simple application it does not work correctly. WHAT! You went through all of this and it does not work. Yes that is correct as this is what I went through with learning a little about events in Flex.
So I started reading and realized that I needed to bubble up the event because the event when dispatched seems to dispatch to the component. I went back and hard coded the bubbles to true. So in the SearchStateChangeEvent I updated the call to the super to look like this now:
public function SearchStateChangeEvent(stateCd:String, stateName:String){super(SEARCH_STATE_CHANGE_EVENT, true);this.stateCd = stateCd;this.stateName = stateName;}
So after running this again it did not work at all, which had me really frustrated so after a little reading and some trial and error. I figured out that the while the event is bubbling up the stack it does bubble across or down. With the DetailResultsComponent component we see it is at the same level as the SearchTypeComponent so when the event is dispatched the DetailResultsComponent never sees it. So when I call the addEventListener it is only looking inside that component, so what I need to do is get the event listener up to the same level where the event is at. Since this is simple I do not have any components inside of components so in this case I need to go up two levels. So in the DetailResultsComponent’s init function we will update it
private function init():void{parent.parent.addEventListener(SearchStateChangeEvent.SEARCH_STATE_CHANGE_EVENT, handleEvent);}
Now the listener will listen above this component and fire off the handleEvent function. After running the application again it runs perfectly. Hooray!!!!
Now when I put this same solution back into our big application it did not work, the reason was that I did not take into account the VBox and Canvas components.
This approach does not seem to be very clean as there is no decopling of the listener and the event. Since I have to know where my component is at relative to the event and then add the parent….addEventListener statement.
A possible solution to this that a coworker pointed out was to have a variable set in like a session and then a changewatcher placed on it but that is a story for another post.
I hope that this has been informative and helpful.

No comments:
Post a Comment