Saturday, September 4, 2010

Quick Dive into Parsley (Basic Injection Example - Part 3)

This is part of a series of articles that should get you up and started using Spicesfactory's Parsley.
This is part 3.
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.  If you are very new to Parsley or new to IOC, you are strongly encouraged to read through Part 2.
In Part 4, we will look at a simple Messaging example.

In this article will look at a very basic example to get the idea of how Injection works in Parsley.

Parsley Basic Injection Example

Parsley works with Flex 3, Flex 4, and Flash.  I will show examples using Flex 3 projects because they can easily be imported into Flex 4, sorry Flash users.  You can still get the idea but I'm going to have to send you back to the docs.  Flex 4 users must note that when building a Flex 4 project and when adding non visual components in MXML, they go in between the <fx:Declarations> </fx:Declartions> tags

Download Parsley
If you haven't downloaded Parsley yet, you should.  You can download it here.  As of this writing the current version of Parsley is 2.3.0.   You can download the source code, but we will be working with the compiled SWC files.

Setting up Parsley

Parsley's zip download includes a release folder.  In that folder you will find five SWC files.  You only really need two of those, one for Spicelib (which Parsley uses) and one for Parsley itself.  Which ones you use, depends on what tool/framework you are using.

For example if you are using Flex 3, you would use the parsley-flex3-2.3.0.swc and the spicelib-flex-2.3.0.swc; if you are using the Flex 4 framework you would use parsley-flex4-2.3.0.swc and spicelib-flex-2.3.0.swc files.

So, now that you have selected the two files that you need, simply copy them to the "libs" folder of your FlexBuilder's project.  That's it.  Parsley is ready to be used in that project.  If you don't have a "libs" folder already in your project, you can go to the Project Properties and select Flex Build Path on the left, then select the Library Path tab on the right side.  Add a swc folder and make sure it is set to Merge into Code as the link type.

Using Parsley
Now that Parsley is ready to go let's look at how you actually use it.  If you recall from the previous article, for Parsley to be able to help you, it needs to be told what objects you wish for it to manage.  You do that with the <ContextBuilder /> tag.  You should include the <ContextBuilder /> in any view that you wish to be the "root" of any context.  For our simple example we will include the <ContextBuilder /> tag in the main application file.  This basically creates something similar to an application wide singleton.  The <ContextBuilder /> tag has several options, but one of the most important is the config option.  Config basically tells the ContextBuilder where is the file that has the description of what objects you wish to have Parsley manage.  The config file can be either MXML or XML.  Having the file in MXML has the benefit of making sure that the classes that you wish Parsley to mange do get linked into the final swf.

So here is an example.  In my main application MXML file I would have the following:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" 
    width="280" height="128" 
    xmlns:views="view.*" xmlns:parsley="http://www.spicefactory.org/parsley"
    viewSourceURL="srcview/index.html"> 
 
    <parsley:ContextBuilder config="BasicExampleContextConfig" />    
</mx:Application>
    
Now let's look at what a Context config file looks like.  This is what my "BasicExampleContextConfig.mxml" looks like:
<?xml version="1.0" encoding="utf-8"?>
    <!--
    This is the file that declares which objects you wish to manage through Parsley.
    Any object declared within the Object Tag will be allowed to participate in any of Parsley's 
    features.  This is a Simple MXML implementation, but this can also be declared in and XML file.
    -->

<mx:Object
    xmlns:mx="http://www.adobe.com/2006/mxml"  
    xmlns:model="model.*">
<!--    
Withing the object tags you declare de objects.  In this case I'm just including the basic 
Model class    
-->    
    <model:BasicModel />    
</mx:Object>

Now if you simply ignore all the comments, it's just and <Object> <BasicModel /> </Object>.
That's it.  Parsley will now create an instance of the class BasicModel and manage that instance for you.
Flex 4 users.  Remember that non visual elements must go in between <fx:Declarations> </fx:Declartions> tags so, the Context config file would look like this.
<?xml version="1.0" encoding="utf-8"?>
    <!--
    This is the file that declares which objects you wish to manage through Parsley.
    Any object declared within the Object Tag will be allowed to participate in any of Parsley's 
    features.  This is a Simple MXML implementation, but this can also be declared in and XML file.
    -->
    
<mx:Object
    xmlns:mx="http://www.adobe.com/2006/mxml" 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:model="model.*">
    
    <fx:Declarations>
            <!--    
            Withing the object tags you declare de objects.  In this case I'm just
            including the BasicModel class    
            -->    
        <model:BasicModel />    
        
    </fx:Declarations>
</mx:Object>


You are probably wondering what is in the BasicModel class.  Well, nothing fancy.  Basic Model can be any class that you want Parsley to manage for you.  When we talk about being managed by Parsley it means that it has access to Parsleys features; that includes: messaging, being created and destroyed automatically, and Injection, among other things.  So let's look at it.

package model
{
    /*********************************************************************
     * BasicModel is a very simple class to demonstrate the Shared Model
     * Concept.  It contains a Bindable String and a Bindable Integer
     * That will be shared by different views.
     * ******************************************************************/
    public class BasicModel
    {
    /*********************************************************************
     * Constructor    
     * ******************************************************************/
        public function BasicModel()
        {
        }

        [Bindable]
        public var sharedString:String;   // The string to be shared
        
        [Bindable]
        public var sharedInteger:int;    // The integer to be shared
    }
}
Notice that the constructor is not taking any parameters. If the constructor was taking parameters, it could still be managed by Parsley and declared in the MXML context config file, but more on that on a different article.

Great, so now we have a class that is being managed by Parsley.  So what can we do with it.  Well, let's inject it somewhere.
I'll create a small view called "ViewOne", which will be a VBox that will hold 3 elements.  The first a CheckBox that will be checked once our object has been injected, the second a TextInput that will be bound to the sharedString of our BasicModel, and finally a NumericStepper that will be bound to our BasicModel's sharedInteger property.
So here is ViewOne:

<?xml version="1.0" encoding="utf-8"?>
<!--
This is a simple view that will have our BasicModel Object injected into it.
-->
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml" width="128" height="82" xmlns:parsley="http://www.spicefactory.org/parsley">

    <!--
    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 will not happen in this view.
    -->
    <parsley:Configure />
    
<!--
    This CheckBox simple get's a check once the Basic Model has been injected.  If no injection has
    occured, the value will be null
-->    
    <mx:CheckBox label="BasicModel Ready" width="100%" enabled="false" selected="{sharedBasicModel != null}"/>

<!--
    The Text input will access the shared string of our model.  I'll use two 
    directional Binding so that it will be updated when it's changed on any other view.
    Note that Flex 3.x Binding is only one way by default.
-->        
    <mx:Binding destination="sharedTextBox.text" source="sharedBasicModel.sharedString" />
    <mx:Binding destination="sharedBasicModel.sharedString" source="sharedTextBox.text" />    
    <mx:TextInput width="100%" id="sharedTextBox"/>
    
<!--    
    The Numeric stepper will access the shared integer of our BasicModel.    I'll use two 
    directional Binding so that it will be updated when it's changed on any other view.
    Note that Flex 3.x Binding is only one way by default.
-->
    <mx:Binding destination="sharedStepper.value" source="sharedBasicModel.sharedInteger" />
    <mx:Binding destination="sharedBasicModel.sharedInteger" source="sharedStepper.value" />
    <mx:NumericStepper width="100%" id="sharedStepper" />


    <mx:Script>
        <![CDATA[
            import model.BasicModel;
   
    /*****************************************************************************
     * sharedBasicModel is the property accesible within this view that will have
     * the shared BasicModel injected into it.  Parsley figures out what to inject
     * because of the Class Type.
     * Parsley doesn't care if it's Bindable or not.  I've declared it Bindable
     * so that I could bind it to some Flex Components
     * The [Inject] metatag is what instructs parsley inject the shared object. 
     * **************************************************************************/    
            [Inject]
            [Bindable]
            public var sharedBasicModel:BasicModel;            
        ]]>
    </mx:Script>
</mx:VBox>

Towards the bottom in the Script section is where Parsley gets involved.  Notice that both the Stepper and the TextBox are bound to a local property called "sharedBasicModel".  It's local right, but above the declaration for sharedBasicModel you have two tags, one the [Bindable] so that binding works and the second one is the [Inject] tag.  Parsley reads this [Inject] tag and looks through the current context to see if it is managing any class of the type "BasicModel", if it is, then it assigns (injects) it's managed object into this local variable.   

VERY IMPORTANT NOTE:  For injection to work, the property to be injected must be public, otherwise it can't be assigned from outside this class.

Now, Parsley doesn't do look for the [Inject] or any other tag in every view, it does this only for the views that you tell it to do so.  And you tell it with the <Configure /> tag.  This tag simply launches a bubling event that Parsley catches and proceeds to manage this view also.  Without this, injection will not work.  Note: You can instruct Parsley to manage All views automatically without the need to use the <Configure /> tag, but more on that on a later time.

That's it.  All you have to do is add this view as child of the view that has the context and it will work.
Notice how there is almost nothing different from how you would do this with manual dependency injection.  Parsley doesn't make you extend anything or write a bunch of code to get things working.

And this is what our projects structure looks like if you copied and pasted the code:


Now that we have a view, well let's go back to the Main app file (in my case it's ParsleyBasicExample1.mxml and add two views to see it work.
Here is the "final" version of the main file.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" 
    width="280" height="128" 
    xmlns:views="view.*" xmlns:parsley="http://www.spicefactory.org/parsley" viewSourceURL="srcview/index.html">
    
<!--    
    This is a very simple application which demonstrates how to set up
    Parsley's managed Dependency Injection.
    
    It uses one shared object named BasicModel that you must instruct Parsley
    that you wish for it to manage it. This is accomplished with the ContextBuilder
    tag found below.  Parsley will ONLY manage objects that you instruct it to do.
    
    In this example there is one class, that has two instances.  Each instance will have
    the shared object injected into them, which means that if you updated the shared 
    object in one view, it will be udated in ALL views that share that same object.
-->    
    <mx:VBox width="95%" height="100%" horizontalCenter="0" horizontalAlign="center">
        <mx:Label text="Parsley Basic Example 1"   fontWeight="bold" fontSize="13"/>
        <mx:HBox x="0" y="0" width="100%">
            <views:ViewOne id="firstInstanceViewOne">
            </views:ViewOne>
            <views:ViewOne id="secondInstanceViewOne">
            </views:ViewOne>
        </mx:HBox>        
    </mx:VBox>
    
<!--    
    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 load the BasicExampleContextConfig file which
    has the "list" of objects you wish to have managed.
    In Parsley you can have nested Contextes, I will show that in another example project.
-->    
    <parsley:ContextBuilder config="BasicExampleContextConfig" />
    
</mx:Application>

Nothing really new Parsley related, just added two ViewOnes.

What happens is that any change done in one view is immediately copied on to the other view.

And here is the application running.  View source is enabled.



It's a very simple example well that doesn't do much more than simply illustrate some of the basic concepts.

You can also download the source code here.

As I have said before, it's fairly easy to take this application out of Parsley.  In the ViewOne all I would need to do is remove the <Configure /> tag.  The main application would look more like this:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" 
    width="280" height="128" 
    xmlns:views="view.*" xmlns:parsley="http://www.spicefactory.org/parsley" viewSourceURL="srcview/index.html">
    
<!--    
    This is a very simple application which demonstrates how to set up
    Parsley's managed Dependency Injection.
    
    It uses one shared object named BasicModel that you must instruct Parsley
    that you wish for it to manage it. This is accomplished with the ContextBuilder
    tag found below.  Parsley will ONLY manage objects that you instruct it to do.
    
    In this example there is one class, that has two instances.  Each instance will have
    the shared object injected into them, which means that if you updated the shared 
    object in one view, it will be udated in ALL views that share that same object.
-->    
    <mx:VBox width="95%" height="100%" horizontalCenter="0" horizontalAlign="center">
        <mx:Label text="Parsley Basic Example 1"   fontWeight="bold" fontSize="13"/>
        <mx:HBox x="0" y="0" width="100%">
            <views:ViewOne id="firstInstanceViewOne" sharedBasicModel="{myBasicModel}">
            </views:ViewOne>
            <views:ViewOne id="secondInstanceViewOne" sharedBasicModel="{myBasicModel}">
            </views:ViewOne>
        </mx:HBox>        
    </mx:VBox>

<mx:Script>
    <![CDATA[
        import model.BasicModel;
        var myBasicModel:BasicModel = new BasicModel();
    ]]>
</mx:Script>
    
<!--    
    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 load the BasicExampleContextConfig file which
    has the "list" of objects you wish to have managed.
    In Parsley you can have nested Contextes, I will show that in another example project.
-->    

<!--   Removed for demonstrating a non Parsley implementation
    <parsley:ContextBuilder config="BasicExampleContextConfig" />
-->    
</mx:Application>

Notice that I commented out the <ContextBuilder />  tag and had to declare a variable (myBasicModel) that I had to inject manually into both views, but the underlying structure did not change.  The point I wanted to illustrate here is that to take advantage of Parsley you don't have to follow a specific patter or structure, Parsley just makes your life easier and it will adapt to any structure or patter you wish to implement.

Well, that's it for this example.  In the next example we will take a look at messaging.

70 comments:

  1. Hi, can you please explain how to do this: "Note: You can instruct Parsley to manage All views automatically without the need to use the tag, but more on that on a later time."

    ReplyDelete
  2. Sure,
    You change a little the Context Builder
    You would use something like:
    <parsley:ContextBuilder>
      <parsley:ViewSettings autowireComponents="true"/>
      <parsley:FlexConfig type="{MyConfig}"/>
    </parsley:ContextBuilder>

    You then can specify the views you wish to manage in the context config file, and the view get's configured automatically (without the <Configure /> tag.
    Something like: <View type="{MainView}"/>

    If you really want ALL views, which I would discourage you to do so, you would override the filter function to accomplish that.

    There is more info on this specifically in chapter 8.4 of the docs.
    http://www.spicefactory.org/parsley/docs/2.3/manual/

    Or straight to the chapter
    http://www.spicefactory.org/parsley/docs/2.3/manual/view.php#config_automatic

    ReplyDelete
  3. All working fine there by applying this to my project. I liked this easy first example, demonstrating basic things, but which I'll need to use dozens of time in the future, so better to have it "right". Thanks !

    ReplyDelete
  4. Great to hear that Jahz, glad to be of help. Keep reading on and you will start seeing even more cool things that you can apply to your projects.

    ReplyDelete
  5. Ah just one additional question: by default, I would have used



    instead of the two bindings you're defining.
    But if I understand well, this would mean that the binding is only "one way", the TextInput would have been refreshed automatically as expected, but when typing smth into it, this wouldn't have updated the shared model and thus the second view, right ?
    Is there no other to define this bi-directionnal binding, inside the metatag for example ?

    Anyway, I'll go on with the next cool things ;)

    ReplyDelete
  6. I was meaning:
    TextInput width="100%" text="{sharedBasicModel.sharedString}"

    ReplyDelete
  7. Jahz,

    You are correct. The default text binding is a read option, not write.

    In Flex 3.5 that is the "easiest" way to do two-way binding, but not the only way. For example you could set up the two way binding in ActionScript using BindingUtils. Also you could set a one way binding to the text input, and use the Change event to update the value. In the Presentation model example I use that method (http://artinflex.blogspot.com/2010/11/presentation-model-implemented-in.html).

    In Flex 4 you can set two-way binding a little easier. You use the @ symbol.

    For example:
    <TextInput id="sharedTextBox" text="{sharedBasicModel.sharedString}"/>
    is one way "read" binding.

    <TextInput id="fahrenheit" text="@{sharedBasicModel.sharedString}"/>
    is two way read/write binding.

    ReplyDelete
  8. Hi, well I'm using Flex 4, thanks for the tip, that's perfect!

    ReplyDelete
  9. Nice work, Art! Very helpful. Thanks for taking the time to write these blog entries.

    ReplyDelete
  10. I am new to flex. while going thru this tutorial I could not figure out how to create the "Object" tag...

    do i create it as an MXML component or module or application or itemRenderer, when u right click and say "NEW"... tried all these methods but object tag would not show up under mx except under fx namespace..

    ReplyDelete
  11. contd... for the previous question posted by anonymous...

    I am trying to create this object










    Thanks

    ReplyDelete
  12. /mx:Object
    xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:model="model.*">



    </mx:Object

    ReplyDelete
  13. @Anonymous,

    Yeah, it's not obvious. Basically you can create any MXML component, just pick Canvas. Then go into the source view, select all the existing text and replace it with the text from the BasicExampleContextConfig.mxml example.

    ReplyDelete
  14. Good work Art!
    Its a great article for anyone who is new to Flex or just parsely.

    Thanks,
    PReddy

    ReplyDelete
  15. Could you please explain how the Method Injection works? The official Parsley documentation only mentions it, but doesn't give any clue to what this feature might be used for!

    Thanks!

    ReplyDelete
  16. Thanks PReddy,

    Sure sansegot. The name makes it sound like you are injecting a method, but that's not the case. It's injection of value through a method.

    Normally, for Injection to work, the property needs to be Public, but for example, let's say that you wish to make it private, or you wish to have some code triggered whenever the value is injected. That what Method injection is good for.

    So you place the [Inject] tag just before a public function. Parsley will look at the property types that the function (method) expects and will inject according to that.

    For example :
    private var myService:LoginService;
    private var serviceReady:Boolean = false;
    [Inject]
    public function setTheService (service:LoginService,) : void
    {
    myService = service;
    serviceReady = true;
    }


    So Parsley will look in the context for some object of type LoginService to inject into this function.

    That's it. Basically the same applies to Constructor injections, except that Constructor injection might require some changes in the context config file.

    ReplyDelete
  17. Sorry about the extra comma after service:LoginService it should not be there.

    ReplyDelete
  18. Art, thank you for one more great explanation!

    The Parsley documentation is written for people already familiar with the concepts the framework is trying to solve. Without your articles I don't think I would ever start using it!
    Thanks to you now not only do I understand the concepts, but I'm starting solving problems in my project with Parsley!

    I really owe you a beer!

    ReplyDelete
  19. Thanks, I'll take you up on the beer someday.

    ReplyDelete
  20. Dear Art, your blog is spamming me. I left a comment once and now every new comment is sent to me (not sure that I even requested this!). If I click "Unsubscribe" it takes me to a page trying to persuade me to sign up for a Blogger.com account! Arrgghhh!!!! SPAAAAAMMM please help!

    ReplyDelete
  21. Hi John,

    Sorry about that. I had no clue blogger did that, and honestly don't know how to stop it as it's nothing in the settings, it's a blogger thing. Another reason why to move away from blogger.

    I googled a bit and found this: Hope it helps.
    http://www.google.com/support/forum/p/blogger/thread?tid=331aee3bc5575ec6&hl=en

    The blogger account would allow you to manage your subscriptions, but that is lame that it forces you to do that. I'm really sorry.

    I'll go add a comment to someone else blog so I can find a way to unsubscribe.

    ReplyDelete
  22. Does parsley allow to inject a view in another view? For example, say we have two implementations of a view (say DefaultNavigationPanel and BUNavigationPanel). If business specific implementation is available we will inject NavigationPanel1 else we will inject DefaultNavigationPanel.

    ReplyDelete
  23. Although it's not a common scenario, I assume it would work just fine. Do notice that Injecting it, does not mean that it will be added to the stage. That still would have to be done manually, after it was injected.

    ReplyDelete
  24. Hi Art,

    Thank you so much for these wonderful articles on Parsley. Do you have a printable format of all these articles? It is tough to get this printed out from these web pages.

    Thanks,
    Vij

    ReplyDelete
  25. Hi Vij,

    Thanks. Unfortunately I don't have them in a printable format. Sorry.

    -Art

    ReplyDelete
  26. Art, you and your little art. Magic.

    Thank you ever so much.

    ReplyDelete
  27. Hi Art

    am new to flex & this article is a gift to me.
    Thnak u so much.....

    - Priya

    ReplyDelete
  28. Hi Art, thanks for writing this sets of tutorials.
    I have a question on dependency injection.
    I have 2 classes A and B which inject the same object of the type C (a VO class). Everything works as expected until I try to assign the injected variable to a new object instance in one of the classes (say A). Now the injected variable in the other classe B is not updated to the new instance, instead its properties are always the same as before.

    public class A
    {
    [inject]
    public var c:C;

    public function reset():void
    {
    c = new C();
    //after this the injected variable c in class B is not updated
    }
    }

    public class B
    {
    [inject]
    public var c:C;
    }

    Is this behaviour as expected and are there any workarounds?
    Thanks.

    Regards,
    Yang

    ReplyDelete
  29. @Yang.
    Hi, yes, that is the expected behavior. The "workaround" would depend on what are you trying to accomplish.

    For practical purposes Parsley creates an object of type C. It is assigned to your public var c in class A. When you invoke c = new C(); you basically remove the referece to the object managed by Parsley, so any changes made to the new object will not be reflected on any shared object.

    If you wish to clear all the values, simply have some method in the VO that clears the values, but does not change the address of the underlying object.

    Hope that helps.

    ReplyDelete
  30. Thanks Art. I really appreciate your help. Actually your suggestion is what I'm doing currently. My concern is that as I'm currently working with Blazeds, I need to have an object returned in the result event handler to be mapped directly to the value object I have in flex. Before integrating with parsley I just needed to say something like vo = data as VO; (vo is the injected variable). Now this won't work as it won't be reflected on other classes sharing this object. Does it mean I have to convert every attribute in the vo line by line now? Are there any good practices regarding this?
    Again thanks for your help, I really have learned a lot from you.

    Regards,
    Yang

    ReplyDelete
  31. Hi Yang,

    Great question!

    Disclamer: I've always considered best practices to be something very subjective, what is best for me, may not be best for you, nor everyone in every case.

    The built in Flex data/services don't fit in to well with MVC because they create a model and a VO shoved into a VO. It's cool for simple things, not that great for more complex things.

    So if you really wish to get the most out of your MVC architecture, then yeah, you need to copy properties.

    Now there are some things to take into account. Flex/Flash does not always copy contents, but just copies references, depending on the datatype. For example a ByteArray will not get copied, it copy a reference to the original memory location.

    Anyways, I have found best to have the "copy" code in one place of the VO so that I don't have it spread all over the place in case I add/remove properties easily. I've also found it useful that in the constructor I check the datatype being passed and automatically make a copy of the object if it receives the specified type.

    Now, speaking of MVC, VO's are usually not injected directly, more often the model is what is injected. The model holds the VO's. This is a bit different from how Flex/Data Services are by default, since the VO is a Model and a VO.

    What makes a Model a Model is that it holds state. Holding state is what makes Models cool. Using a Model to simply go get a record and discard it immediately after is overkill.

    What I mean by holding state is that you will hold that data for more than just fetching it. It can be simultaneously used elsewhere, or in the future as a cache or as a reference to see if data has changed, in other words, you care about the current state of the data, and the data can change state (values).

    Hope that helps.

    ReplyDelete
  32. Also would you share your thoughts on using commands(e.g. dynamic commands in parsley) VS using the Spicefactory Task Framework?

    Basically I want to use the Task framework for 2 reasons: a) I want to put a set of tasks into a group and execute them sequentially or concurrently without couplings between the tasks; b) I want to use the cancel() function for tasks and taskgroups, so that an event indicating the completion of the task won't be always dispatched.

    One use case of b): I have a RemoteLoginCommand (dynamic command), in the result() function I dispatch an LoginedEvent to indicate a successful login. Somewhere else exists the messageHandler for the event, which tells the application to change to loggedIn state. Now if the user cancel the login while waiting for the response from the server, I don't want the LoginedEvent in the command class to be dispatched any more because as the user has already canceled the login, I don't want the user to be taken to the loggedIn state even when the response is received. The solution I can think of is to add a flag in the command class indicating whether the login request has been canceled. Then after the user cancels the login, dispatch another event which is listened by the command class, which will then change the flag, which will then stop the LoggedInEvent from dispatching.

    With this approach I need to have a lot of events dispatching and handling, but the classes are still decoupled.

    If I were to use the Task framework, I can simply do loginTask.cancel() and the result handler won't be executed anymore. This is quick but the tasks are now coupled with the class that uses them, since the start() and cancel() functions have to be called explicitly.

    What approach would you recommend?

    I apologize if my description is not clear to you, otherwise I'm really enjoying the discussion with you on these topics.

    Regards,
    Yang

    ReplyDelete
  33. Hi Art, I tried to post a comment (before the last one) but it's always been deleted immediately after it's published. Do you know why that happens?

    ReplyDelete
  34. I'll try it again.
    OK that makes sense. I'm not implementing a strict MVC architecture, but presentation model instead. But I guess the underlying concepts are similar. A few questions:

    I'm not sure if I fully understand your interpretation of the difference between a model and a VO. From my understanding, a model in the presentation model architecture is usually mapped to a corresponding view. So although they don't know the existence of the views which use them, they are more or less just the state holder of the view (and often they have 1:1 relationship). Whereas with VOs they don't have the explicit relationship with a particular view and can be used by presentation models and commands, etc, as needed. Is this a reasonable understanding?

    ReplyDelete
  35. You said that "VO's are usually not injected directly". What would be a common approach to use shared VOs in different models/commands if they are not declared to be managed by Parsley in the context file and injected in the PM and command classes?

    Regarding "have the "copy" code in one place of the VO so that I don't have it spread all over the place in case I add/remove properties easily", are you saying I should have a method like updateProperties(?,?,?,?,?…) where I pass all the properties in the class which I want to update into the list of parameters and set the properties in there? For example, say I have just received from the server an object called data with the same properties as the VO, since I can't simply do vo = object for reasons you mentioned previously, instead I would do vo. updateProperties(data.id, data,name, data.address......)? Is this what you mean or are you talking about something totally different?

    ReplyDelete
  36. @Yang,

    Your previous comments where sent to the possible spam folder, maybe you double clicked by accident and it was flagged as being sent twice or who know. Anyways.

    Your first question was also asked in:
    http://artinflex.blogspot.com/2010/10/quick-dive-into-parsley-dynamic-command.html

    Actually you can combine both Task Framework and dynamic commands.
    Check out
    http://www.spicefactory.org/forum/viewtopic.php?p=2510&sid=dc5e09ae3e861eebface7a1ec75ccca2

    A model does not have to be tied to a view unless it is specifically a presentation model, but that's just one type of model.

    What makes a model a model is that it holds state, regardless if it's tied to one or accessed by many views (tied to none).

    On the other hand you have Value Objects (VO). Now VO's are used in many different ways. In the default Data/Services of Flex, the actually hold state, which makes them a model.

    To me, a VO is strictly an object that holds data, similar to a struct of C/C++, but can do validations, etc... but the VO does not hold state.

    A model can host an array of VOs or a previous and current VO, or a history of VOs.

    Check out:
    http://artinflex.blogspot.com/2010/11/presentation-model-implemented-in.html

    That might help a bit.

    With the update properties, yeah. But not a long list of parameters.

    For example you can have a very generic:

    public function updateProp(source:Object)
    {
    if (source.hasOwnProperty("name"))
    this.name = source.name;
    if (source.hasOwnProperty("url"))
    this.url = source.url;
    if (source.hasOwnProperty("tel"))
    this.tel = source.tel;
    }

    So you just pass another VO to that VO, to have a single source to update it's values from. Similar concept as how you implement a clone() function. Of course you can use something specific rather than an Object and have type checking for you. And your clone() function can call the updateProp() function and have all that in one single place.

    Hope that helps.

    ReplyDelete
  37. Thanks Art. That helps.
    SO if I have a VO (say UserVO) which holds the information for a User after logging on, should I be passing the vo around through events or injecting the VO in the places where I need it? This vo will be persistent during a login session.

    Regards,
    Yang

    ReplyDelete
  38. Following the question above, I'm basically not able to decide when to use injection and when to pass objects by event dispatching. For example, if I combine the task framework and dynamic command, should I be injecting tasks in a command class or just create tasks as local variable in the execute function, like the example shown in the Spicefactory forum?

    ReplyDelete
  39. Yang,

    There is no one "perfect" solution for everything. Different needs, different solutions.

    Your example fits great for using the Model concept. Rather than passing a UserVO around. You can have a "Session" model that you inject. The Session model holds State. It holds whether the user is logged on or not. Being logged on might give access to some areas, not to others. If the user is logged on, then you can have fields that describe the user. These don't necessarily need to match the "Transport" VO that you got from the server. The UserVO retrieved from the server can be used to update more than one Model. For example a "Preferences" Model, or a "Permisions" Model, not just the Session Model.

    See, your Client models do not need to match your data models, and they don't need to match your server requests. They can, but they don't have too.

    The concept relies on have a State that can change. For example if the user is logged in or not.

    Going MVC is a bit weird at first if you are used to the concept of:
    Need data -> Get data -> use data -> ditch data.

    Under MVC, you view could care less about processing the data, what it cares about is the State of the data. The view will inject the data that it cares about. It might choose to have that data "refreshed", which triggers a command. The command will choose how to refresh the data, it might call the service. The service will return a VO which the command (or the service, depending on your choice) uses to update the model. The model let's the interested active parties know that it has updated data, through messaging or binding (which is messaging anyways), and the view gets the data from the model, not from the command.

    What if you have multiple views that adjust behavior or display information dependent on the user being logged in. If the user logs in from anywhere, all you have to do is update the model and all the view get "magically" updated.

    The model may or not have a VO in it. I use VOs if the data needs structure and if I'm going to have more than one record of that same data, for example a list of users, etc... But a session model will rarely ever have a VO in it.

    A great benefit with this concept is that if you choose to dramatically change server technologies, changing the remoting VO's would not have an impact on the rest of the project.

    Check out:
    http://artinflex.blogspot.com/2010/10/quick-dive-into-parsley-dynamic-command.html
    for a very simple example of that concept.

    Now, going this way does generate more work, and you need to work on being consistent so that you can follow it in the future.

    For simple one/two view projects it's just plain overkill, but good for practice and learning.

    ReplyDelete
  40. Thanks Art. I have gone through the whole sereis of your tutorials by the way, and I constantly consult them since they contain concrete examples.

    I understand MVC. It's just that when using the Presentation Model variation, a model (PM) is usually specific to a view or a set of similar views in terms of the data they need. Now if I have a Session model, I can't use it as the PM for any views directly as the data in the Session model is probably not enough for any views. Therefore in the PM of a view I have to retrieve relevant data from the Session model (possibly injected) and updates it when instructed by the view.

    Speaking of VO, there seems to be some confusion on the difference between VO and DTO.
    http://www.adam-bien.com/roller/abien/entry/value_object_vs_data_transfer
    I assume that when we talk about VO we generally refer to DTO as defined in the page above. Then when I have a Session model, in a Presentation Model scenario, what differentiate it from a VO? If I understand you correctly, since a model holds states and a vo doesn't, a vo should then be immutable (or at least not saved and shared by multiple classes through injection). Rather they would simply be used as data containers (or DTOs) which are passed around through events or remoting.

    Again thanks for sharing.

    ReplyDelete
  41. Regarding the task framework/dynamic commands integration, this seems to be the major functionality improvement for Parsley 3, which will deprecate the Task lib and introduce a new Command lib which makes chaining and grouping commands a lot more natural.

    ReplyDelete
  42. Yang,

    Your are correct. In your PM you would inject your other models. Basically a PM separates the View from the framework.
    In Flex 4 you have skins which sort of acomplish the same goal, just not at the same level.

    For example with Robotlegs, you "should" use mediators for every view. A mediator is similar to a PM (except that the view just does not know about the mediator, but could if you really wanted to). In Robotlegs, a view does not have a built in way to inject anything to it, it should be done through mediators.

    Parsley is more flexible in that sense. It doesn't impose any pattern. You may or not use PM or MVC or anything, you can just use whatever you want and mix and match.

    Should you use PMs? Hmmmm... It depends on your project. The REAL truth is that it is very unlikely that you will switch views and keep the same PM in most project. Even if you are building a Mobile and desktop view, you will probably end up making some adjustments to your PM anyways. It is nice to have them apart when you make some changes, but it is more work to use them.

    About VO's. Yes the commonly used VO term refers to DTOs. I usually call my enums as enums probably because I come from a C/C++ world.

    And yes. I personally think that PM come handy when there is state involved, for example a data entry form, or any type of view that changes the value of data. You can even implement multiple Undo's, which is great for the user, and have an array of VOs (DTO) in which the state for each undo is stored (for example).

    In my opinion using PM for simply displaying a list of tweets (for example) is just overkill, but if you hold state such as which you keep a history of the order in which the user viewed the details of the tweets, then you could justify it.

    Yeah, I saw that news entry. I look forward to Parsley 3.

    ReplyDelete