Thursday, November 4, 2010

Quick Dive into Parsley (Fast Inject - Part 10)

 This is part of a series of articles that should get you up and started using Spicesfactory's Parsley by Jens Halm.. It includes examples on how to use Parsley for Flex.
This is part 10.
In Part 3, I showed how to set up Parsley and showed a Parsley basic injection example.
In Part 4, I showed how to send messages using Parsley that do not extend the Event class.
In Part 5, I showed how to send messages using Flex Events.
In Part 6, I showed nested contexts and object lifecycle methods.
In Part 7, I showed Decoupled Bindings
In Part 8, I showed how to use Dynamic Commands & Dynamic Objects
In Part 9, I showed how to use Automatic Component Wiring.

This article builds upon what was covered in the previous examples, so if you haven't seen them, I encourage you to start with at the beginning of this series.  This article focuses on Parsley's Fast Inject tag.


Fast Inject
   In Parsley, when you wire a component to the Context, Parsley takes a deep look at that component through reflexion (examines all it's properties, not the values) and proceeds to identify it, not just by name but by what it is, and what it is made of.  This allows for example the ability to inject interfaces, and Parsley knows what classes actually implement that interface.

  Well, this inspection isn't free.  It consumes resources: time and memory.  View components usually inherit tons of properties, so there is lot's to reflect on.  On a small application the impact is basically not noticeable, but on large applications with tons of view components wired to the context, it will have an impact.  This is where <FastInject> comes in handy.  Fast Inject allows for injection without reflection.  In other words, you can still inject managed objects onto a view, without the component being wired to the context, and therefore not paying the price.

  So why not <FastInject> everything? Well, since the component isn't wired to the context, it can't participate in all of Parsley's features, for example it can't participate in Messaging.  Although you can get around the dispatching, you won't be able to use Message Handlers.  Basically you can only Inject with <FastInject>.

  Because of this limitation <FastInject> works great when you combine it with a Presentation Model.  This example will not cover the Presentation Model, as that is a pretty wide subject that is independent of <FastInject>, you do not need to use the Presentation Model to use <FastInject>, and you don't have to use <FastInject> to create a Presentation Model, they simple work well together.

  Ok, so how do you use <FastInject>?

  It's simple, in any MXML component you just place a <FastInject> tag and declare to which variable you wish to inject to.  So for example, you declare in the script section of your component the property:

public var myLocalModel:LocalModelClass;

   And in the mxml section of your component you place:

<parsley:FastInject property="myLocalModel" type="{LocalModelClass}" />

   That's pretty much all you have to do.  Now the Parsley managed object of type LocalModelClass will be injected into your local myLocalModel property, and the view component will not go through reflection.

  Ok, let's say that you still want to use the lifecycle methods?  Well, since the component isn't wired to the Context you don't have access to them, but... you can still find out when injection is complete.  You can do that by following this syntax:

<parsley:FastInject injectionComplete="injectionCompleteHandler();">
  <parsley:Inject property="myLocalModel" type="{LocalModelClass}"/>
</parsley:FastInject>

   The nice thing about this syntax is that you can add multiple fast injects toguether and have the complete handler called after all injections are complete.  So for example you could have the following properties declared in the script section:

public var myCustomerModel:CustomerModelClass;
public var myOrdersModel:OrdersModelClass;
public var myUserStatusModel:UserStatusClass;

   And you could have the following injections declared in the mxml:

<parsley:FastInject injectionComplete="injectionCompleteHandler();">
  <parsley:Inject property="myCustomerModel" type="{CustomerModelClass}"/>
  <parsley:Inject property="myOrdersModel" type="{OrdersModelClass}"/>
  <parsley:Inject property="myUserStatusModel" type="{UserStatusClass}"/>
</parsley:FastInject>

The example

  Time to see it running.  Here is the <FastInject> example with source enabled:



   Here is the structure of the project:

   It is very much like the previous example.  A very simple model called SimpleModel and two views.  CustomViewOne has a simple <FastInject> with no injection complete handler and CustomViewTwo has a <FastInject> with an injection complete handler.

   So let's take a look at the model so you can see what I'm injecting. Here is SimpleModel.as:

package model
{
    /*****************************************
     *  This is a very simple model that holds a string that will be shared
     * among some different views.
     *
     *  For more Flex and Parsley examples visit: artinflex.blogspot.com 
     */
    public class SimpleModel
    {
        /**********
         *  This is the shared string of the Model 
         */
        [Bindable]
        public var sharedString:String = "Hello Parsley";
    }
}

   Basically the same model as before, one string that I plan to share and bind to two text fields.

   Now let's take a look at CustomViewOne.mxml:

<?xml version="1.0" encoding="utf-8"?>
<!--
   Custom View Component used to Fast Inject
   Notice that there is no <Configure /> tag in this file.
   
   For more Parsley and Flex examples visit:  artinflex.blogspot.com
-->
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml" width="200" cornerRadius="5" 
    backgroundColor="#FADEDE" 
    xmlns:parsley="http://www.spicefactory.org/parsley" 
    borderStyle="solid" horizontalAlign="center" verticalAlign="middle" verticalGap="5">

    <mx:Label text="Custom View One" fontWeight="bold" fontSize="12"/>
    
    <!-- Text Input bound to shared string in the shared model -->
    <mx:TextInput width="192" id="sharedTextInput"/>
    <!-- Binding is set to be bi-directional  -->
    <mx:Binding destination="sharedTextInput.text" source="sharedModel.sharedString" />
    <mx:Binding destination="sharedModel.sharedString" source="sharedTextInput.text" />
    
    <!-- Fast Inject performes Injection without wiring the view to the context -->
    <parsley:FastInject property="sharedModel" type="{SimpleModel}" />    
    
    <mx:Script>
        <![CDATA[
            import model.SimpleModel;
            /***********************************************
             * Shared Model injecected into this view.
             */
            [Bindable]
            public var sharedModel:SimpleModel;
        ]]>
    </mx:Script>    
</mx:VBox>

   In this view, I have a simple <FastInject>.  Notice that <Configure> is not being called.  I declared a public variable of type SimpleModel that I set the <FastInject> to inject the Parsley managed object to.

   Now let's look at CustomViewTwo.mxml:

<?xml version="1.0" encoding="utf-8"?>
<!--
   Custom View Component used to demonstrate Fast Inject
   Notice that there is no <Configure /> tag in this file.
   
   For more Parsley and Flex examples visit:  artinflex.blogspot.com
-->
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml" width="200" cornerRadius="5" 
    backgroundColor="#EAFADE" 
    xmlns:parsley="http://www.spicefactory.org/parsley" 
    borderStyle="solid" horizontalAlign="center" verticalAlign="middle" verticalGap="5">

    <mx:Label text="Custom View Two" fontWeight="bold" fontSize="12"/>
    
    <!-- Text Input bound to shared string in the shared model -->
    <mx:TextInput width="192" id="sharedTextInput"/>
    <!-- Binding is set to be bi-directional  -->
    <mx:Binding destination="sharedTextInput.text" source="sharedModel.sharedString" />
    <mx:Binding destination="sharedModel.sharedString" source="sharedTextInput.text" />    
    
    <!-- Fast Inject performes Injection without wiring the view to the context.
        You can inject multiple properties and have a function called when
        all injections are complete -->
    <parsley:FastInject injectionComplete="init();">
        <parsley:Inject property="sharedModel" type="{SimpleModel}"/>        
    </parsley:FastInject>    
    
    <mx:Script>
        <![CDATA[
            import model.SimpleModel;
            /***********************************************
             * Shared Model injecected into this view.
             */
            [Bindable]
            public var sharedModel:SimpleModel;
            
            /*************************************
             * This function will be called when all injections through
             * FastInject have completed.
             */
            private function init():void
            {
                sharedModel.sharedString += " is Ready";
            }
        ]]>
    </mx:Script>    
</mx:VBox>

   Ok, this one uses the <FastInject> syntax that allows for multiple injects and call a handler when it's done with the injections.  In this case, it calls a function called init() when it's finished with the injections, which then appends " is Ready" to the shared string in the SimpleModel.

  Now let's take a look at the context config file called FastInjectConfig.mxml:

<?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:spicefactory="http://www.spicefactory.org/parsley" 
    xmlns:model="model.*">   
<!---   
   Our simple Model that holds the shared String 
-->   
    <model:SimpleModel />   
</mx:Object>

   Nothing new here, just our SimpleModel since we will not wire any views to the Context.

   And finally, the main app file called FastInjectMain.mxml:

<?xml version="1.0" encoding="utf-8"?>
<!--
  This is a fastinject example.  For more flex and parsley examples visit:
  http://artinflex.blogspot.com
-->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
        xmlns:parsley="http://www.spicefactory.org/parsley"
        xmlns:views="view.*" width="215" height="140" viewSourceURL="srcview/index.html">    
<!---    
    ContextBuilder is how you tell Parsley what objects you want it to manage,
    and how you want it to do it.
-->        
    <parsley:ContextBuilder config="{FastInjectConfig}" />
    
    <mx:Label text="Fast Inject Example" top="3" fontWeight="bold" horizontalCenter="0" fontSize="12"/>
    <views:CustomViewOne y="29" horizontalCenter="0">
    </views:CustomViewOne>
    <views:CustomViewTwo y="86" horizontalCenter="0">
    </views:CustomViewTwo>    
</mx:Application>

   Standard view, creating a standard context.

   Well as you can see, <FastInject> is pretty simple and very useful.  As stated before, it works great for injecting Presentation Models into views, which the next post will be about, but it doesn't have to be used specifically for that.

   Thanks for following this blog and I do hope you find it useful.

   Up next is an example of implementing the Presentation Model pattern using Parsley's messages, Fast Inject and the Message Interceptor.  Check it out here.

6 comments:

  1. Nice one! Really easy to put in place.
    And that's a nice teasing for the PM pattern, which I'm now eager to learn about !

    Thanks for following this blog
    ---> thanks to you !
    and I do hope you find it useful.
    ---> oh yes, no doubt on that point

    ReplyDelete
  2. Do you have (or know where to find) any examples of Parsley's FastInject into an .as file rather than .mxml?

    ReplyDelete
  3. FastInject is, as you have noticed an MXML tag.

    There is no direct as equivalent, but take a look at:
    ContextUtil.findContextInView();
    and
    context.getObjectByType();
    Check out the asdoc:
    http://www.spicefactory.org/parsley/docs/2.3/api/parsley-flex/org/spicefactory/parsley/core/context/ContextUtil.html

    http://www.spicefactory.org/parsley/docs/2.3/api/parsley-flex/org/spicefactory/parsley/core/context/Context.html#getObjectByType()

    That should probably do the trick.

    ReplyDelete
  4. In Parsley 2.4 there is now a FastInject API for Actionscript:
    http://www.spicefactory.org/parsley/docs/2.4/manual/?page=view&section=config_noreflect_api

    ReplyDelete
  5. @sunild Thank you. I need to update this all to Flex 4 and Parsley 3.0 I haven't had a chance yet. But I'll find some time soon.

    ReplyDelete