Saturday, September 4, 2010

Quick Dive into Parsley (Basic Messaging Example - Part 4)

This is part of a series of articles that should get you up and started using Spicesfactory's Parsley.
This is part 4.
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.

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

In this article will look at a very basic example of how messaging works in Parsley.

Parsley Basic Messaging Example


What is Messaging?
   Messaging under the context covered here is the ability for two classes/components to communicate between each other without explicitly calling each others methods.  So basically "messaging" involves some decoupling, and that is the basic idea that drives messaging.

   Flash provides a fairly powerful and flexible messaging mechanism which is Flash  Events.  Flex adds Flex Events and Binding.  Yes, Binding is a messaging mechanism as it allows to invoke methods of other component's without calling them explicitly, in a very transparent way.  But Binding is focused on one specific task, notify that data has been updated, which is useful, and it's incredibly simple to set up.  The "problem" with Binding is that in the process of decoupling methods, you couple properties.  Now I'm not saying that Binding is bad, actually I think it's great, it is simply not something to be used everywhere.

  Flash and Flex Events are great.  From the sender's perspective, it's totally decoupled.  The class/component that needs to send the message does not need to know anything about who is listening.  All you do is dispatch an event.  Now from the receiving part is where it's not entirely decoupled.  In Flash and Flex, to be able to catch a message you must be listening for messages, and you implement this through event listeners. Event listeners, must be attached to some instance of a class/component which can be the the object that dispatched the event.  Now, events in Flash have the ability to "bubble" and what means is that you can add event listeners to the view parents of the object that dispatched the event and still catch the message.  Flash and Flex events have the capability to travel through the view hierarchy and be caught elsewhere.  So this still provides some decoupling but you still have to attach your listeners to some instance of that is part of the view hierarchy.  You could go for the root, which is the Application object, to decouple, but this means that you can't have more specific scopes.  Although Flash / Flex events are great, there is still a small gap to be filled, and that's where Parsley's messaging comes in.

Parsely's Messaging
  As discussed above, Parsley comes in to fill the gap and not only provides fully decoupled senders, it also provides fully decoupled receivers.  One of the great things about how Parsley implements it's messaging mechanism, is that it not only decouples the sender from the receiver, it decouples both from the Parsley framework itself.  You do not need to extend any class, nor you need to attach/bind/assign listeners to any specific object.  Also, Parsley's messaging is generic, it is not aimed towards any specific pattern.  So you can totally take advantage of Parsley's messaging to implement any MVC pattern or any other pattern and not have to extend/add/change any of Parsley's classes to do so.  It's simple and very flexible.  Additionally, you can use extend Flash/Flex events, but you do not have to.

So let's take a look at how it works.  You can have Parsley manage specific Flex events, or you can use simple classes to send messages.

We will begin with a simple class.  You can look at the docs on how to have Parsley manage standard Flex events.

Dispatching Messages
   Essentially you should begin by creating a class that will hold your message.  In this particular case, I'm building a simple application that logs every message sent and displays it in a listbox.  Here is the basic structure of our application.  You might notice that I'm not following convention regarding package names, this is on purpose to make the example much easier to follow.

So here is the messaging class called LoggerMessage.as

package messages
{
    /***************************************************************
     * 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 does not 
     * extend Flexs Event.  It is inteded to be used for a log in 
     * future.
     * */ 
    public class LoggerMessage
    {
        /********************
         * 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 LoggerMessage( message:String)
        {
            _message = message;
        }
        
        /*********************************
         * read only property that delivers the message.
         * */
        public function get message():String
         {
             return _message
         }
    }
}

There is nothing special about this class.  It simply has a read only property which is the message itself that can only be defined when you instantiate the class.

Let's move on to the SendView.mxml file which is the view that actually sends the message.  There is a few things that you should notice.  There is a <Configure /> tag which if you recall from the previous article, it tells Parsley that you want it to manage this view.  There is a [MessageDispatcher] tag which we didn't see in the previous article.  This tells Parsley that you want it to inject a message dispatching function into the variable declared immediately below.  This function will help us dispatch non Flex Event messages.  Here is the code for SendView.mxml.

<?xml version="1.0" encoding="utf-8"?>
<!--
This is a simple view that will send messages
-->
<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">

    <!--
    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 CheckBox simple get's a check once the dispatcher has been injected.  
    If no injection has occured, the value will be null
-->    
    <mx:CheckBox label="Sender Ready" enabled="false" selected="{dispatcher != null}"/>
    
<!--
    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" enabled="{dispatcher !=null}" click="handleButtonClick(event)"/>

    <mx:Script>
        <![CDATA[
            import messages.LoggerMessage;
        
    /******************************************
     *   In this example we will be using Parsley's Message dispatcher 
     *   which does not require the use of FlexEvents.  This takes 
     *   decoupling a little bit further, since the receiving end of the
     *   message will not have an event.target option in which they can
     *   access the sender.  This can also be declared in MXML.
     *   Important NOTE: Dispatcher does not have to be Bindable. I made
     *                   it Bindable so I could bind it to the checkbox and 
     *                    button for this example. 
     ***/            
             [MessageDispatcher]
             [Bindable]
            public var dispatcher:Function;
            
            private function handleButtonClick(event:MouseEvent):void
            {
                if (dispatcher != null)
                 {
                     dispatcher(new LoggerMessage(messageTextBox.text));
                 }
            }   
        ]]>
    </mx:Script>
</mx:VBox>

Take a look at the function executed when the button is pressed.  It first checks if the variable of type Function called dispatcher is not null.  This is the variable in which the  [MessageDispatcher] got injected into.  The message dispatcher function expects one argument, the instance of a class (any class) that you wish to send.  The keyword here is "any" in any class.  So I create a new instance of LoggerMessage with the text from the TextBox as the message.  That instance of the message will be sent to any class that wishes to receive it.  You can have more than one listener.  That's it for dispatching messages, now on to the receiving part.

Message Handlers
Receiving messages is quite simple and it is the same whether the message was sent using Parsley managed FlexEvents or the MessageDispatcher.  You must simply put a [MessageHandler] tag above the function declaration that you wish to get called.  Note that you will also have to include the <Configure />  tag to have Parsley manage this view.  Let's take a look at 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 messages.LoggerMessage;
        
            /**************************
             * 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:LoggerMessage):void
                {
                    if (receivedMessages == null)
                       receivedMessages = new ArrayCollection();
                    receivedMessages.addItem(logMessage.message);
                }
            
        ]]>
    </mx:Script>
    
</mx:VBox> 

  Notice towards the bottom there is a function called handleLogMessage(...) above that there is the [MessageHandler] tag.  How does Parsley know when to call this function?  Simple, by the datatype that it is expecting.  Any message sent through the Parsley dispatcher, or declared to be managed by Parsley, that is of the type LoggerMessage, will be received by this function, and Parsley will call this function for you.

So here is the app running.  View source is enabled.



Well that should get you started with Parsley.  I most certainly encourage you to go and read the docs, as this barely scratched the surface of what Parsley does.

In the next article, we look at this same example but using Managed Flex/Flash Events.

21 comments:

  1. Just read the complete series of Parsley articles that you posted. I was looking for a starting point and also the good reasons that why should I try Parsley and got all my queries responded in your articles. Meanwhile, I posted the counterpart of your Flex 3 examples in Flex 4. Anybody can get them from here: http://wp.me/p139v7-3

    Thanks,
    MD

    ReplyDelete
  2. Thanks, I'm glad they were of help. I've done the Messaging example with FlexEvents which I'm just finishing the note, so it should be on in a day or two. I also have plans for a Sub-context example and a complete yet simple MVC example. That should get anybody pretty much rolling. Parsley's docs are very complete but they do assume you know what he is talking about, and that's not always the case. Thanks for the port to Flex 4.

    ReplyDelete
  3. So, instead of running bunch of .addEventListener in our own code we can replace that with [MessageHandler] tag. It is indeed feels decoupled, but don't you think under the hood there have to be listeners created inside Parsley message handling components anyway. So problem is solved with the same solution. Ideally, to address issues with Adobe's Event and Binding they have to be decoupled at the framework level. Parsley is only a quick and dirty fix, albeit very elegant one! My 2c.

    ReplyDelete
  4. For any integrated system to be "integrated" at some point in time there must be coupling. The same applies with framework based Injection.

    It will eventually become coupled, otherwise it simply won't integrate and it will be pointless.

    The concept is that the framework itself handles the coupling and not you. You write loosely coupled code, and let the framework worry about the coupling.

    The important difference between .addEventListener and [MessageHandler] is that in your code you have to specify what object you want to listen to directly with .addEventListener. That is fine and simple when you want to listen to events on the same component, but when the dispatching component is nested into 2 or 3 levels, and the listening component is nested 2 levels on a different parent branch, then that becomes somewhat of a pain to specify the object you wish to listen to. Flex helps you with this, using Event Bubbling, as the event will travel the display list, but it won't bubble through to other view branches, so you still must know an ancestor they both (dispatcher and listener) have in common. No matter what, using .addEventListener your code on the listener must know at minimum the view hierarchy of the dispatcher

    Using [MessageHandler] your code on the listening side doesn't really care about where in the view hierarchy the dispatcher is, the only thing they both care is what type the message data is. Bubbling is no longer a factor. In fact, Parsley messages don't bubble, it acts more like a central switch that notifies listeners when data of the type they are expecting is ready, regardless of who sent it. Who sent it is irrelevant, unlike .addEventListener

    ReplyDelete
  5. Also, Parsley doesn't actually have to use addEventListeners under the hood when you don't use standard Flex Events for messages, as shown in this example. It simply calls functions that are associated with that data type. No need to involve Flex Events, which is one of the things I really like about Parsley.

    Also an alternative to using FlexEvents is using AS3 Signals.
    http://github.com/robertpenner/as3-signals/wiki
    But using Parsley's decoupled events as shown in this example is similar to using signals when you Inject the signal.

    One important thing to note, when using Parsleys' messaging without events, and the same applies to as3Signals, is that when you dispatch a message, the handler will get called immediately, before the dispatching function completes it's execution. They are not queued, to be called later. That might affect your logic under some circumstances.

    ReplyDelete
  6. Working fine, succesfully applied this on my app.

    However, I'm a bit puzzled by your last comment about execution order. And not sure to understand why it would behave differently with a Flex Event ?

    As for the fact that it's all based on data "type", how are you doing when your app does need several objects of same type ?

    ReplyDelete
  7. Good to hear that Jahz,
    The execution order. Flex event are normally asynchronous, while Parsleys messaging is synchronous. What does that mean, and what is the difference?

    Asynchronous messaging means that when you dispatch the event the functions that are targeted to react to that event are not called immediately. They get queued to be called later. So when you call for a dispatch of a Flex event, the line of code that follows the line that made the dispatch will be executed before the handler function gets called.

    Synchronous messaging means that the handler functions get called immediately when you dispatch the function, so when you dispatch a message using Parsley, the function handlers are called before the line of code that follow the line that dispatched the message.

    Which is better? Well, both have benefits. Obviously the Synchronous is faster, but the Async is more friendly with event driven platforms. Flash is very event friendly.

    ReplyDelete
  8. Thank you for your wonderful efforts... I now understand the flow of parsley.

    ReplyDelete
  9. Hi Hissam,

    Sorry for the late response, your post was in the Spam box, so I didn't noticed it until now.

    If you look at the comment above the code it explains why. It says: "No config file was defined because we are not asking Parsley to manage any particular object."

    In this example there is no Model. The views themselves become part of the context with the Configure tag so they should NOT be included manually in the context.

    Why are you getting an error? It would help if you could tell me/us what the error is that you are getting. But for starters, in my project the class is called LoggerMessage, not MessageLogger.

    Regardless, you should not add a class to the Context in the config file, and also add it with the Configure tag.

    ReplyDelete
  10. Great to hear that Amrit, thank you.

    ReplyDelete
  11. Hi Art

    Im a little confused about where i can put my receivers , in your example the receiver is inside a View.

    But in my case i trying to use this kind of messaging between the main appl, modules and libraries.

    can i set the receiver inside a Flex library in order that some views, services , dynamic commands can dispach a message?..

    Best Regards

    ReplyDelete
  12. Drazz,

    You can put receivers anywhere you want as long as that object becomes part of the context, either being included in your context config file or in a view that is added with the Configure tag.

    They can be in modules or in libraries.

    That said, here are a few notes:
    First: Parsley doesn't work well with libraries loaded as RSLs. It's not Parsley's fault. It's a weirdness of the compiler. When creating a Final release, the compiler simply removes all the custom tags from the libraries and I haven't found a way to stop that. I really haven't tried this with FB 4.5, it might be solved, or not.

    Second: When using an MVC pattern. It is considered 'bad' practice to have listeners in your Model. That's why you have Commands. If you follow the series here, it will show how to use Commands. That said, Parsley will not enforce this, it's really up to you.

    ReplyDelete
  13. I do not see a source although you say 'source is enabled'. Hence I do not see source of main application. Just putting 2 views in the main app does not set dispatcher. Please advise how to get entire source for this app.

    Dusan.

    ReplyDelete
  14. @Dusan

    If you right click on the demo app near the bottom of the article you will get an option in the pop up menu that says: View Source

    That's what view source enabled means.

    To get your messaging working, you need to create a context on the main file so that your views have something to attach themselves to.

    Here is an extract from the source:
    <!-- ContextBuilder is how you tell parsley to manage objects that are not Views. If you wish to have Parsley manage a View, you do that through the Configure tag. The Configure Tag is being used in the ViewOne component. In this case I'm instructing Parsley to create a context. No config file was defined because we are not asking Parsley to manage any particular object. We just need to create at least on context. -->
    <parsley:ContextBuilder />

    ReplyDelete
  15. It works in Firefox as as expected but my try was in Chrome where it does not work; right click on the application does not give anything - try.
    Thanks, Dusan.

    ReplyDelete
  16. Dusan,
    Thanks for the update. Good to know that doesn't work in Chrome.

    ReplyDelete
  17. Very usefull tutorial.
    But I have a question about how the function receiver [public function handleLogMessage(logMessage:LoggerMessage)] which 's based on type message, can distinguish between one situation where I need to add a LogMessage to another where I need to delete a LogMessage ?

    ReplyDelete
  18. @Mira. There are different ways you can approach this. One is to actually have a DeleteLogMessage type for messages you wish to delete, and another function would handle it.

    If you wish to have the same function and same Class ("LoggerMessage") do both, then you must adjust the LoggerMessage class to add some extra info. You would need to set some flag (boolean) that says if it's going to Delete the message, and some unique identifier for the message so that it can be correctly located and deleted. Then in your function you would check the flag and you would look at the identifier to locate the object.

    The example on the post is quite simple, but since the messages are being stored in an ArrayCollection you could identify them since they are not being discarded, but using some sort of ID would not be a bad idea.

    ReplyDelete
  19. This is a great series for parsley beginners!


    Just for information:

    I tried this example in flex 4.5, parsley 2.4.1 and i did not get running it until i added

    to the application.

    ( with quasi empty parsleyContext - File )

    ReplyDelete
  20. <parsley:ContextBuilder config="{parsleyContext}" />

    is missing in previous post, sorry

    ReplyDelete
  21. Hey man. I LOVE YOU!

    rsrs..

    Thank you very much.

    ReplyDelete