Thursday, September 9, 2010

Quick Dive into Parsley (Messaging with Flex Events Example - Part 5)

This is part of a series of articles that should get you up and started using Spicesfactory's Parsley.
This is part 5.
In Part 1, I explained the reasons as to why we chose to use Parsley in our projects.
In Part 2, I explained what Parsley is and some basic concepts and how they relate to Parsley.
In Part 3, I showed how to set up Parsley and showed a basic injection example.
In Part 4, I showed how to send messages using Parsley that do not extend the Event class.

This article builds upon what was covered in Part's 2, 3, and 4 so if you haven't seen them, I encourage you to start with Part 1.

In this article will look at the same basic messaging example in Part 4 but in this case we will use managed Flex Events, rather than Parsley's dispatcher.

Parsley Messaging with Managed Flex Events Example
 So we looked at the basics of messaging in the previous note which involved creating a custom message class.  This class did not have to extend any Flash/Flex Event, and the message was dispatched using Parsley's Message Dispatcher.   This is not the only way of dispatching messages with Parsley.  Parsley can also manage standard Flash/Flex events.  You basically setup and dispatch an event like you would normally do in Flash/Flex and dispatch it.  Parsley will then manage that event and invoke any function set as a [MessageHandler].  Do note that once Parsley manages an event, it will no longer bubble.

  They way you tell Parsley to manage an event is with the [ManagedEvents] metadata tag.  First off, for Parsley to be able to know about an event, the event must be declared with the standard Flash/Flex Metadata tag [Event] .  You should get in the practice of doing this, regardless if you use Parsley or not, this allows the Flex compiler to be able to recognize the events as MXML tag attributes.  A quick example of how you use the [Event] tag is:
[Event(name="Log Message", type="events.LoggerMessageEvent")]
  You can declare your event tags in an pure ActionScript class before the class definition but within the package.  On an MXML component you can declare them in the <Script> block or within the <mx:Metadata> tags.  I think that in MXML component's it is best to declare them in the <mx:Metadata> section.

  Once you have your events declared, following those events you would place the [ManagedEvents] tag where you would specify the events you wish to manage.  Here is an example:

<mx:Metadata>
 [Event(name="Log Message", type="events.LoggerMessageEvent")]
 [ManagedEvents("Log Message")]

</mx:Metadata>

   Once that is set up, you dispatch your event like you would do usually, by calling the standard dispatchEvent() function.

   Ok, enough talk, let's go into the code.

   I've made some class name changes to reflect events, so here is the new structure.  I'm aware that I'm not following Flexs' convention where it comes to naming the packages.  I've structure the packages in a way that it's much easier to follow along.

   Let's begin begin by looking at the LoggerMessage clas, this class has been modified to extend the Flash Event class, so I renamed the class accordingly, it's new name is LoggerMessageEvent.  Here is the code to LoggerMessageEvent.as:

package events
{
    import flash.events.Event;
    
    /***************************************************************
     * This is a simple class that will hold a message so it can be 
     * delivered thought our application.  It only has one property
     * that is the message itself.  Note that it  
     * extends Flash Event.  It is inteded to be used for a log, sometime in 
     * future.
     * */ 
    public class LoggerMessageEvent extends Event
    {
        
        /********************
         * String used to identify our event
         * */
        public static const LOG_MESSAGE:String = "Log Message";
        
        
        /********************
         * This is the private var that holds our message
         * */
        private var _message:String;
        
        
        /********************
         * Constructor that takes the parameter as message
         * @param message  is a String that contains the text that will be set
         * */         
        public function LoggerMessageEvent(parType:String, message:String)
        {
            super(parType);            
            _message = message;
        }
        
        /*********************************
         * read only property that delivers the message.
         * */
        public function get message():String
         {
             return _message;
         }


        /*********************************
         * When you extend the Event class you should always override the
         * close class to make sure your custom event propagates properly. 
         * */        
        override public function clone():Event
         {
             return new LoggerMessageEvent(type,message);
         } 
        
    }
}

Basically the only changes are those required to extend an Event class.  Do note that I change the name of the package to reflect that it is an event.

Now let's look at the new SendView.mxml:

 <?xml version="1.0" encoding="utf-8"?>
<!--
This is a simple view that will send messages.  It is part of an example on how to
use Parsley.  Visit http://artinflex.blogspot.com for more info.
-->
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml" width="128" height="86"
     xmlns:parsley="http://www.spicefactory.org/parsley" horizontalAlign="center" cornerRadius="2" borderStyle="inset">

    <!--
      If you wish to have Parsley manage your custom events extended from Events,
      they must be declared as metadata.  They can be in the script section or
      in between metadata tags in the MXML.  The first line is a stardard Event
      declaration that you should get into the habit of including regadless of
      using Parsley or not.  The second line [ManagedEvents] is what actually tells
      Parsley that it should listen for and take control of these events.
    -->
    <mx:Metadata>
        [Event(name="Log Message", type="events.LoggerMessageEvent")]
        [ManagedEvents("Log Message")]
    </mx:Metadata>
   
    <!--
    Configure is very important.  Configure dispatches an event that instructs Parsley that you 
    wish to have this view Managed by Parsley.  If you do not include this tag or do not launch
    the configure event, Injection nor Parsley messaging will not happen in this view.
    -->
    <parsley:Configure />
   
   
<!--
    The Text input is not tied to anything
-->
    <mx:TextInput width="100%" id="messageTextBox"/>
   
<!--
    The button will dispatch our events.
-->   
    <mx:Button label="Send Message"  click="handleButtonClick(event)"/>
   

    <mx:Script>
        <![CDATA[
            import events.LoggerMessageEvent;
       
    /******************************************
     *   In this example we will be using Parsley's Managed Events.
     *  This basically allows you to write standard FlexEvents that can
     *  easily be used in any other framework.  You simply dispatch your
     *  custom event like you would normaly do in Flex.  You simply have to
     *  have the [ManagedEvents] Metadata tag declared.  Look at the Metadata
     *  section of this file.
     ***/
            private function handleButtonClick(event:MouseEvent):void
            {
                dispatchEvent(new LoggerMessageEvent(LoggerMessageEvent.LOG_MESSAGE,messageTextBox.text));
            }
  
        ]]>
    </mx:Script>
</mx:VBox>
  

  To begin with, notice the <MX:Metadata> section at the top of the file, which was discussed at the beginning of this note.  Also note that you will no longer see a [MessageDispatcher] tag or function as these are no longer needed.  Because there is no dispatcher function injected, I removed the binding of the enabled property of the button with the function not being null.  Lastly note towards the bottom in the <Script> section that you now dispatch the message like you would dispatch any Flash/Flex event, with the dispatchEvent() function.

  You can read more about Flex custom events in Adobe's livedocs site.

  Now the ReceiveView really had no reason to change.  The only change I made was regarding the change of the LoggerMessage class into LoggerMessageEvent.  Other than that it's essentially the same class.
Here is ReceiveView.mxml:

<?xml version="1.0" encoding="utf-8"?>
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml"  width="140" height="112"
     xmlns:parsley="http://www.spicefactory.org/parsley" horizontalAlign="center" 
     cornerRadius="3" borderStyle="solid">
    <mx:Label text="Received Messages" fontWeight="bold"/>
    
    <!--
    Configure is very important.  Configure dispatches an event that instructs Parsley that you 
    wish to have this view Managed by Parsley.  If you do not include this tag or do not launch
    the configure event, Injection nor Parsley messaging will not happen in this view.
    -->
    <parsley:Configure />    
    
     <!--
     This is a simple list component that will display the messages that will be received.
     -->
    <mx:List id="messageDisplayList" dataProvider="{receivedMessages}" 
              width="100%" height="86" x="0" y="0" ></mx:List>
    
    <mx:Script>
        <![CDATA[
            import mx.collections.ArrayCollection;
            import events.LoggerMessageEvent;
        
            /**************************
             * Private property that holds an ArrayCollection of the messages that have 
             * been received.
             * */
            [Bindable]         
               private var receivedMessages:ArrayCollection = null;
               
               /**************************
                * This function will receive any message that is of the type
                *  LoggerMessage Notice the MessageHandler tag.  That instructs
                *  Parsley that you wish to route any dispatched message of 
                * type LoggerMessage to this function.  Parsley figures this 
                * out because of the data type of the parameter of the function 
                * automatically.  MessageHandler can also be declared in MXML.
                * */
               [MessageHandler]
               public function handleLogMessage(logMessage:LoggerMessageEvent):void
                {
                    if (receivedMessages == null)
                       receivedMessages = new ArrayCollection();
                    receivedMessages.addItem(logMessage.message);
                }
            
        ]]>
    </mx:Script>
    
</mx:VBox>

And here is the app running with ViewSource enabled.  The basic functionality is the same as the previous article.



That's it for this note, hope it was helpful, in the next couple of notes we will look at some other features of Parsley.

Next is an example of Parsley Nested Context and Object lifecycle methods.

9 comments:

  1. Very nice explanations. Hope you do more and keep the simplicity.

    ReplyDelete
  2. Thanks. Yes there are more on the way. I'm just finishing one on Nested Context and Object life-cycle.
    I work on these at home when the family is sleeping, so it goes a bit slower than I would like but it will continue. Already have 3 inline.

    Also noted your example at:
    http://www.lonhosford.com/lonblog/2010/09/14/basic-parsley-framework-flex-example-explained/
    which is great, it combines in a simple case, what I have posted here. It also includes the [Init] tag which I go into in the next example.

    My goal is to keep the simplicity, focus on Parsley and not on building a cool gadget app. to make things a lot simpler to follow.

    ReplyDelete
  3. Thank you so much. I like your simple way of explaining things. Keep up your great job.
    Yes I would be very interested to learn more about the Nested Context and Object life cycle.

    ReplyDelete
  4. Sorry it's taking so long for the new post. It's almost ready.

    ReplyDelete
  5. Working great like previous one without Flex events.

    Still, I'm not sure about what should drive the decision about going for a Flex Event or not.
    Inregards to this small example, they just seem to achieve the same thing, with some minor differences on how to program it, but what's the real difference ? What are the key points to remember when making this decision ?

    Cheers!

    ReplyDelete
  6. Well, like most things, it's a matter a which you like best. I like Parsley messaging better, keeping in mind that it is synchronous and if I daisy chain to many things together the application may loose responsiveness.

    Why do I like Parsley messaging over Flex Events? Because it doesn't depend on Event types bases on string identifiers. I get type checking by the compiler by default, and refactoring is much easier. Also there is a lot less boilerplate code.

    Why use Flex Events? It makes it easier to take Parsley out your application if you choose to do so in the future. They are asynchronous.

    ReplyDelete
  7. Thanks for the explanation, added to the one on the previous note, it's all clear.

    ReplyDelete
  8. I have a
    * Main.mxml
    * DueInEvent.as
    * DueInController.as

    in Main:

    [Event(name="DueInEvent", type="mifield.events.DueInEvent")]
    [ManagedEvent("DueInEvent")]



    <![CDATA[
    protected function getDueInData(event:Event):void
    {
    dispatchEvent(new DueInEvent(DueInEvent.DUE_IN_DATA_RQST));
    }
    ...
    -------------------------------------------
    In event:
    I have all the right stuff... with clone override
    -------------------------------------------
    In Controller
    [MessageHandler]
    public function handleDueInServiceDataRqst(message:DueInEvent):void
    {
    if (message.type == DueInEvent.DUE_IN_DATA_RQST) {
    trace("Controller: due-in data requested.");
    service.getData(); // a svc call
    }
    }
    ...
    ...
    -------------------------------------------

    However, the event is not getting handled
    Now in Main.mxml if I call the

    [MessageDipatcher]
    public var dispatcher:Function

    protected function getDueInData(event:Event):void
    {
    dispatcher(new DueInEvent(DueInEvent.DUE_IN_DATA_RQST));
    }

    Then everything works...can you say why ?

    ReplyDelete
  9. If you copied and pasted your code, then the issue is with ManagedEvents tag, it should be plural not singular.

    It should be:
    [ManagedEvents("DueInEvent")]

    ReplyDelete