Saturday, November 27, 2010

News Update

   I apologize for being absent for a short while.

   My youngest daughter got Pneumonia and ended up in ICU, she was in the hospital for a whole long week, which made me fall behind in everything.

   A customer of us wanted to update his website, but wanted to use WordPress as his content management system.  We came up with some cool ideas and ways to integrate things to their site and make it easy for them.  I was new to WordPress, and my PHP was a bit rusty (we usually use .net as the backend), so besides being backedup in work, I also had something new to learn (WP) and refresh (PHP).

   So anyways, time to work on this blog was very lacking.  But now I'm back on track.

   When I started this blog I was pretty new to the writing blogs thing, I grabbed the easiest and quickest thing I could find, which is blogger, and started off.  After playing around with WordPress, I've been missing out so........  much.  Blogger is ok, it is certainly easy, but the editor has some serious issues where it really screws some things up.  I got to the point of writing everything in Word, and copy and pasting to blogger, just to keep a copy in case blogger decided to nuke the contents.  Well, there is another benefit of using Word.  English is not my first language so it helps a bit with the grammar.  I have a serious problem with using it's instead of its.  I know the proper use, but that Pinky finger just loves to push that apostrophe.  Word helps me notice those little typos.  But it does take me more time.

  The other thing I truly hate about blogger, which is somewhat related, is they way the editor handles links.  If I want to have a link to open up a new tab window, I have to manually go and edit the html every single time.  I wish the cursor would actually stay in the same place (ala Dreameaver), but nooo... as the post gets longer, this becomes even more of a pain.  Last, the editor every once in a while is unsuccessful in saving your changes, but it tells you it did.  The only way to know for sure is to navigate away from the page.  If you get a warning, then it isn't saved.  I once lost an entire post because of that reason.  If a post is getting long, I tend to copy the html code to notepad, just in case it decides to nuke the post again.

   So... what does that mean?  Thanks to you, I'm moving the blog to a better place, to a permanent place.  I have enjoyed building this, and I'm very pleased to see that it has helped some people, so I'm going to continue with it and take it up a notch.

  The new home isn't ready yet, and no, it will not be hosted on wordpress.com, but I will use the WordPress software.  So when the new site is up, you will be the first to know.  I will make a last post on this site, which I was already working on before the Hospital visit.

   One thing I must say that it is freaking easy to do with blogger, is to add ads to your blog.  I didn't have much personal experience in that department, knew how to implement it, but no clue on the return value.  That's something I've been asked multiple times before, but could not provide a real answer.  A lot of people think that they can be rich just by setting up a website.  Well, truth be told, adding adsense to the blog didn't make it profitable.  To give you some clue... you make about US $0.13 per 1000 visits, at least that's my experience.  I could plaster a whole lot more ads, and probably improve that a bit, but that is not the goal, the goal here is to help developers like you, the ads where just a tool to learn a bit more, and well, they paid for the registration of the domain name.  :)

   I do have a question though, and please leave answers in the comments.  This came up from a comment in one of the posts.  Would you consider purchasing a downloadable/printable PDF with all this material and some more for say US$5?  Or a book (CD Included)?  (ok. no publisher would accept the $5 on a book price tag I think)  Let me know.  Thanks again.

Sunday, November 14, 2010

Presentation Model implemented in Parsley

   This is an article about implementing the Presentation Model pattern in Flex using the Parsley framework.  It includes an example with source.

   Back in 2004, Martin Fowler published an article about the presentation model.  That article is pretty much the reference that most people use when discussing the Presentation Model.

   In 2007 Paul Williams published a port of Martin Fowler’s example in Flex.   Now, I took Paul Williams’ example and ported it into Parsley.

   If you are completely new to Parsley I suggest you take a look at a series of articles I wrote to get the basic concepts.  http://artinflex.blogspot.com/2010/09/quick-dive-into-parsley-intro-why.html

What is the Presentation Model?
   The Presentation Model is a pattern in which you separate your application logic and state from the user interface components.  Basically, you take the logic and state of your application out of your view components.

   I have seen some confusion on the net about how the Presentation Model (PM) pattern relates with the MVC (Model View Controller) pattern.  The most obvious difference is that MVC includes a Controller, which the plain Presentation Model doesn’t.  The second difference is in the concept of what a Model is.  In both, the Model holds business logic and state, but the difference is that usually the MVC Model is more data oriented, while the PM model is more view oriented.  Now there is absolutely nothing that says that you can’t combine the PM pattern and the MVC pattern, you most definitely can combine them.  Just because usually  the models in an MVC implementation model the data, it doesn’t mean that you can’t also have models that model the view.

   So what are the benefits of implementing a presentation model?  The first benefit is that it allows you to run tests on your logic without a view, you can much easier run unit test on the presentation model.  It also makes it easier to port your code to other devices or other frameworks, or completely change your UI elements without affecting the logic.  As the application grows in functionality, having the functionality broken into discrete classes also helps keep things organized and easier to update.  It can also help reducing copying and pasting code, as you can have the presentation model broken into several classes and have common things to your views in one class that is injected in multiple views

   What is the drawback?  A little more work at the beginning.  Also, the more portable you create the presentation model, the less likely that you will take advantage of some of Flex’s cool tools, like Validators for example.

   Is it worth using the Presentation Model?  Hmmm… that’s a complex answer.  As most of everything, it depends.  In the simplest of projects it is just not worth the extra work.  As the application logic gets more complicated, then it’s worth it.  Flex 4 uses somewhat of this PM concept with its new Skinning model.  In Flex 4, you are separating the view components from the logic, except that Flex 4 promotes a bi-directional relation between the skin and the skin container, while in the classical PM pattern only one needs to know of the other (UI and Model), but it doesn’t have to be bidirectional.

   The truth is that it is quite possible that you don’t reuse a whole lot of your views, but you probably copy and paste a lot of code between your views, for validation, formatting, data acquisition and saving, etc…  Well, that’s when the presentation model begins to be useful.  Also, it eventually becomes easier to add more business/application logic.


   Martin’s and Paul’s examples implement the Presentation Model as a singleton, where there is ONE presentation model for the view.  While this is ok for that example, you might not always want to keep the state of your views persistent.  In my example I use Dynamic Objects so that each view can have its own PM.

   If you really want to get a deeper knowledge of what the presentation model is, you should read Martin Fowler’s article at: http://martinfowler.com/eaaDev/PresentationModel.html

And check out Paul’s at: http://blogs.adobe.com/paulw/archives/2007/10/presentation_pa_3.html

Standard Flex Presentation Model example by Paul Williams
   My example is based entirely on Paul’s example, so it is a good idea to take a look at Paul’s example.

You can find a running version of Paul’s example at:
http://examples.pmwilliams.co.uk/adobeblog/presentationpatterns/presentationmodel/PresentationModel.html

And you can view the source code at:
http://examples.pmwilliams.co.uk/adobeblog/presentationpatterns/presentationmodel/srcview/index.html

I also suggest you take a look at the notes, especially since the code isn’t commented.  It will help you figure things faster.  Here are the notes:
http://examples.pmwilliams.co.uk/adobeblog/presentationpatterns/presentationmodel/srcview/source/notes.html

Briefly explained, there are five main classes involved.

Extracted from Paul’s notes:
PresentationModel.mxml - this is the root of the application and is only responsible for 'bootstrapping' activities such as instantiating the root view component and the root presentation model object.

AlbumBrowser.as - this is the presentation model for the root view. It encapsulates the behaviour and state of the album browser view. This component instantiates and collaborates with an instance of the AlbumForm class.

AlbumForm.as - this is the presentation model for the album form view. It encapsulates the behaviour and state of the album form view.

AlbumBrowserView.mxml - this is the root view component. It is injected with an instance the AlbumBrowser presentation model. The view renders the AlbumBrowser instance to the screen, and observes it for changes.

AlbumFormView.mxml - this is the view for the album form. It is injected with an instance of the AlbumForm presentation model. The view renders the AlbumForm instance to the screen, and observes it for changes.

And here is the class diagram provided by Paul with the note:
Presentation Model Class Diagram in Flex by Paul Williams

Flex Presentation Model using Parsley example
   Now Paul actually uses dependency injection, but not framework managed. To replace Paul’s version with a Parsley dependency injection, it’s really simple.  The only change would be to use one <FastInject>, declare the context and not assign the AlbumBrowser to the AlbumBrowserView’s  albumBrowser property in the PresentationModel main view.

  But Parsley is so much more than a simple dependency injection framework.  There is a lot more we can gain from a few more changes.

   First off, in Paul’s version as you can see in his diagram, there is a one to one relation between the AlbumBrowser model and the AlbumForm model.  Additionally they are tightly coupled together, which means that the AlbumFormView is tightly coupled to the AlbumBrowserView through their presentation model, which complicates testing components individually.  You also could not have two simultaneous AlbumFormViews, so you could not easily turn this into a Multi-Document-Interface app.

   To decouple both PM, and create the possibility of turning this app easily into and MDI app, you can easily accomplish that with Parsley Messaging, and that’s exactly what I’ve done.


   So I kept the basic functionality as it is, but added a whole lot more of potential, by changing a couple of things and taking advantage of Parsley’s features.  A particular Parsley Feature not discussed previously in my series of articles of Parsley is the [MessageInterceptor] tag, which I take advantage in this example.

   I took the liberty of renaming some classes and properties to make it easier to follow the code.  For example Paul’s AlbumForm which is a model I renamed it as AlbumFormModel.  I also changed the layout to make it fit the blog layout better.

 Here is my class diagram after my changes:

Presentation Model Class Diagram using Parsley and Flex
   As you can see, the basic structure is similar, but how things are glued together is different.  In my case, the Context holds both models.  Since I wanted the possibility (implemented in the next post) of having multiple AlbumFormViews simultaneously, and each one with its own AlbumFormModel (Presentation Model) then I declared the AlbumFormModel as a Dynamic Object in the context, which means that the context can have multiple (n) AlbumFormModels.  Since the AlbumBrowserModel is a singleton, it can be shared by multiple AlbumBrowserViews.  Both models are injected into the views with <FastInject> which means that the views themselves are not reflected uppon.  I did not change how the views are instantiated, so although it has the structure to become an MDI application it isn’t; this is to keep it somewhat simple.

   So let’s take a look at the running app (View source is enabled):



   It behaves pretty much like Paul’s, same functionality, more potential.

Let’s look at the project structure itself:

   As mentioned before, the model classes where renamed to make it easier to follow the code.
 Note that the Observer utilities have been removed as they are no longer needed, that was a pretty clever class from Paul.

   There is a new message class called: SelectedAlbumChangeMessage which is dispatched whenever a new album is selected from the list.

   Also new is the Context config file which holds both Model classes.

We still have:
PresentationModelMain - this is the root of the application and is only responsible for 'bootstrapping' activities such as instantiating the root view component and the root presentation model object.

AlbumBrowserModel - this is the presentation model for the root view. It encapsulates the behaviour and state of the album browser view. It no longer instantiates and collaborates with an instance of the AlbumFormModel class directly. It is now done through the SelectedAlbumChangeMessage being dispatched.

AlbumFormModel - this is the presentation model for the album form view. It encapsulates the behaviour and state of the album form view.

AlbumBrowserView - this is the root view component. It is injected with an instance the AlbumBrowserModel presentation model. The view renders the AlbumBrowser instance to the screen, and observes it for changes.

AlbumFormView - this is the view for the album form. It is injected with an instance of the AlbumForm presentation model. The view renders the AlbumForm instance to the screen, and observes it for changes.

So now let’s look at the code:

I'll start with the views.

First let's take a look at the root, the AlbumBrowserView.mxml:

<?xml version="1.0" encoding="utf-8"?>
<!---
  This is the AlbumBrowser root view.  It contains little logic.  The login
  is implemented in the Presentation model that is injected into this view.  
      
  This is comes from Martin Fowler's post about Presentation Model, which
  was then ported to Flex by Paul Williams.  I adapted it to show the
  same example implemented usign the Parsley Framework
      
  Martin Fowler http://martinfowler.com/eaaDev/PresentationModel.html
  Paul Williams http://blogs.adobe.com/paulw/archives/2007/10/presentation_pa_3.html 
  Arturo Alvarado http://artinflex.blogspot.com/ 
-->
<mx:Panel 
    xmlns:mx="http://www.adobe.com/2006/mxml"
    xmlns:view="com.adobe.ac.view.*"
    xmlns:util="com.adobe.ac.util.*"
    title="{ albumBrowserModel.windowTitle }"
    width="410" 
    height="460"
    layout="vertical"    
    verticalScrollPolicy="off"
    horizontalScrollPolicy="off"
    styleName="albumBrowser"
    xmlns:parsley="http://www.spicefactory.org/parsley" >
    
    <mx:Script>
        <![CDATA[
            import com.adobe.ac.model.AlbumBrowserModel;
            import com.adobe.ac.vo.AlbumVO;
            
            import mx.controls.Alert;    
            import mx.events.CloseEvent;
            import mx.managers.PopUpManager;        
            
            [Bindable]
            public var albumBrowserModel : AlbumBrowserModel;
            
        ]]>
    </mx:Script>
    
    <!-- Use Fast inject so that this view isn't wired into the context -->
    <parsley:FastInject property="albumBrowserModel" type="{AlbumBrowserModel}" />
    
      <mx:List
        id="browser"
        width="381" 
        height="227"
        borderStyle="solid"
        dataProvider="{ albumBrowserModel.albums }"
        selectedItem="{ albumBrowserModel.selectedAlbum }"
        labelField="title"
        change="albumBrowserModel.changeSelectedAlbum( AlbumVO( browser.selectedItem ) )"/>
        
    <view:AlbumFormView 
        id="form"
        width="390"    />    
</mx:Panel>

   As you would expect from an application using the presentation model, there isn't anything too fancy here.  I <FastInject> the AlbumBrowserModel into a local var called albumBrowserModel.  Then through binding, it updates itself from it's albumBrowserModel.  It notifies the PM (albumBrowserModel) through the change event calling specific methods of the PM so that the PM knows that the view has changed.   This view hosts an AlbumFormView, which is the other view.

  Ok, so let's look at it's Presentation Model, here is AlbumBrowserModel.as:

package com.adobe.ac.model
{    
    import com.adobe.ac.delegate.AlbumDelegate;
    import com.adobe.ac.messages.SelectedAlbumChangeMessage;
    import com.adobe.ac.vo.AlbumVO;    
    import flash.errors.IOError;    
    import mx.collections.ArrayCollection;
    import mx.collections.Sort;
    import mx.collections.SortField;    
    import org.spicefactory.parsley.core.messaging.MessageProcessor;
    
    /**************************
     * This is the presentation Model for the AlbumBrowser root view.
     * It encapsulates the behaviour and state of the album browser view.
     * 
     * This is comes from Martin Fowler's post about Presentation Model, which
     * was then ported to Flex by Paul Williams.  I adapted it to show the
     * same example implemented usign the Parsley Framework
     * 
     * Martin Fowler http://martinfowler.com/eaaDev/PresentationModel.html
     * Paul Williams http://blogs.adobe.com/paulw/archives/2007/10/presentation_pa_3.html 
     * Arturo Alvarado http://artinflex.blogspot.com/ 
     */    
    [Bindable]
    public class AlbumBrowserModel
    {
        /****************
         * The current list of albums that are selected
         */
        public var albums : ArrayCollection;        
        
        /*****************
         * The title displayed on the window
         */
        public var windowTitle : String = "My Albums";
        
        /******************
         * The currently selected album of the list
         */
        public var selectedAlbum : AlbumVO;
        
        /******************
         * Message dispatcher used to notify that the current album 
         * selected has changed
         */
         [MessageDispatcher]             
        public var messageDispatcher:Function;

        
        /**************************
         * Constructor
         * It calls the delegate to fill the list af albums and
         * then proceeds to sort them by title name
         */
        public function AlbumBrowserModel()
        {
            var delegate : AlbumDelegate = new AlbumDelegate();
            albums = delegate.getAlbums();
            albums.sort = new Sort();
            albums.sort.fields = [ new SortField( "title", true ) ];
            albums.refresh();            
        }
        
        /**********************
         * Called whenever the user selects a new album from the list.
         * It proceeds to dispatch a message that indicates the current/previous
         * album and the newly selected album.  It updates the selectedAlbum
         * property to reflect the change.
         */
        public function changeSelectedAlbum( album : AlbumVO ) : void
        {
            if ( album != null )
            {
                var selectedAlbumChangeMessage:SelectedAlbumChangeMessage =
                        new SelectedAlbumChangeMessage(selectedAlbum, album);
                
                selectedAlbum = album;        
                if (messageDispatcher != null)
                {
                    messageDispatcher(selectedAlbumChangeMessage);
                }
            }
            else
            {
                windowTitle = "My Albums";    
            }
        }
        
        /************************
         * Receives the selectedAlbumChangeMessage dispatched by changeSelectedAlbum.
         * The message can be intercepted and have the value of cancelled changed before
         * it returns to this view.
         * Currently the AlbumFormModel has an interceptor that assigns that value.
         * If the change is canceled, it returns the selected item to the previously
         * selected item.
         */
        [MessageHandler]
        public function handleAlbumChangeMessage(selectedAlbumChangeMessage:SelectedAlbumChangeMessage):void
        {
            if (selectedAlbumChangeMessage.cancelled)
            {
                selectedAlbum = selectedAlbumChangeMessage.previousAlbum;
                windowTitle = selectedAlbumChangeMessage.previousAlbum.title;
            }
            else
             {
                windowTitle = selectedAlbumChangeMessage.selectedAlbum.title;
                selectedAlbum = selectedAlbumChangeMessage.selectedAlbum;
             }
        }
    }
}

   So the Presentation Model holds the state and logic.  In this case it's holding state by keeping the currently selected album, also the title that the window should display, and the entire list of albums.  It holds the logic because it decides on what to do when the user clicks on a new item in the list.

   When the item is clicked in the list the changeSelectedAlbum() method is called.  It then creates a SelectedAlbumChangeMessage and dispatches it reporting the user intent to change the selected album.

   Now right under that method, you see the handler that handles that same message.  You might think I'm crazy, why would I want to do something as silly as that?  Why not as well just call the method directly?  Well, when I dispatch that message other classes can actually also listen to that message also.  In this case the model for the detail view also can listen and react accordingly.  But even better, Parsley has this wonderful feature called Message Interceptors, with the [MessageInterceptor] tag.  What message interceptors do is that they are called before any registered handler for a dispatched message, and they can change the message content and decide whether to let the message continue or not.

  So in this case note that the handler checks to see if a property of the dispatched message has been set, the canceled property.  The canceled property indicates if the user wishes to cancel the album change, if he/she had changes pending.  If the user decides to cancel the change, the flag will be set and the selected item will be put back to what it was before the user selected the new album.

  Now that decision is not made here, it is made in the detail view (AlbumForm).  This differs on Paul's original implementation, where this model would hold a reference to the AlbumFormModel and check directly if it had changes pending.  The benefit of my implementation is that it decouples both models and it allows to have multiple AlbumFormModels.

   Great, now let's look at the AlbumFormView.mxml:

<?xml version="1.0" encoding="utf-8"?>
<!---
  This is the AlbumBrowser AlbumFormView.  It contains little logic.  The login
  is implemented in the Presentation model that is injected into this view.  
      
  This is comes from Martin Fowler's post about Presentation Model, which
  was then ported to Flex by Paul Williams.  I adapted it to show the
  same example implemented usign the Parsley Framework
      
  Martin Fowler http://martinfowler.com/eaaDev/PresentationModel.html
  Paul Williams http://blogs.adobe.com/paulw/archives/2007/10/presentation_pa_3.html 
  Arturo Alvarado http://artinflex.blogspot.com/ 
-->
<mx:Form
    xmlns:mx="http://www.adobe.com/2006/mxml"
    xmlns:view="com.adobe.ac.view.*"
    horizontalScrollPolicy="off"
    verticalScrollPolicy="off" xmlns:parsley="http://www.spicefactory.org/parsley">

    <mx:Script>
        <![CDATA[
            import com.adobe.ac.model.AlbumFormModel;
            
            [Bindable]
            public var albumFormModel : AlbumFormModel;
        ]]>
    </mx:Script>
    <!-- Use Fast inject so that this view isn't wired into the context -->
    <parsley:FastInject property="albumFormModel" type="{AlbumFormModel}" />
    
    <mx:FormItem 
        label="Artist"
        required="true">
        <mx:TextInput 
            id="artist" 
            width="290"
            text="{ albumFormModel.album.artist }"
            errorString="{ albumFormModel.artistError }"
            change="albumFormModel.updateArtist( artist.text )"
            enabled="{ albumFormModel.canEdit }"/>
    </mx:FormItem>
    
    <mx:FormItem 
        label="Title"
        required="true">
        <mx:TextInput 
            id="title" 
            width="290"
            text="{ albumFormModel.album.title }"
            errorString="{ albumFormModel.titleError }"
            change="albumFormModel.updateTitle( title.text )"
            enabled="{ albumFormModel.canEdit }"/>
    </mx:FormItem>            
    
    <mx:FormItem>
        <mx:CheckBox 
            id="classical" 
            label="Classical" 
            width="290"
            selected="{ albumFormModel.album.isClassical }"
            click="albumFormModel.updateIsClassical( classical.selected )"
            enabled="{ albumFormModel.canEdit }"/>
    </mx:FormItem>            
    
    <mx:FormItem 
        label="Composer"
        required="{ albumFormModel.canEditComposer }">
        <mx:TextInput 
            id="composer" 
            width="290"
            text="{ albumFormModel.album.composer }"
            errorString="{ albumFormModel.composerError }"
            change="albumFormModel.updateComposer( composer.text )"
            enabled="{ albumFormModel.canEditComposer }"/>
    </mx:FormItem>                
    
    <mx:FormItem>
        <mx:HBox>
            <mx:Button 
                id="apply"
                label="Apply"
                enabled="{ albumFormModel.canApply }"
                click="albumFormModel.applyChanges()"/>
            <mx:Button 
                id="cancel"
                label="Cancel"
                enabled="{ albumFormModel.canCancel }"
                click="albumFormModel.cancelChanges()"/>
        </mx:HBox>    
    </mx:FormItem>
</mx:Form>

  Again, little logic here.  The AlbumFormModel is injected with <FastInject> so that the view doesn't go through reflection.  The components are bound to a local copy of the album, using one way data binding.  When the user updates the content in the TextInput, though the change event a method in the model is invoked that registers that there was a change.  It also get's the text for the errors from the model.

Let's look at the presentation model, here is AlbumFromModel.as:

package com.adobe.ac.model
{
    import com.adobe.ac.messages.SelectedAlbumChangeMessage;
    import com.adobe.ac.vo.AlbumVO;
    
    import mx.controls.Alert;
    import mx.events.CloseEvent;
    
    import org.spicefactory.parsley.core.messaging.MessageProcessor;
    
    /**************************
     * This is the presentation Model for the AlbumBrowser's AlbumFormView.
     * It encapsulates the behaviour and state of the AlbumFormView.
     * This component instantiates and collaborates with an instance of the 
     * AlbumForm class.
     * 
     * This is comes from Martin Fowler's post about Presentation Model, which
     * was then ported to Flex by Paul Williams.  I adapted it to show the
     * same example implemented usign the Parsley Framework
     * 
     * Martin Fowler http://martinfowler.com/eaaDev/PresentationModel.html
     * Paul Williams http://blogs.adobe.com/paulw/archives/2007/10/presentation_pa_3.html 
     * Arturo Alvarado http://artinflex.blogspot.com/ 
     */
    [Bindable]
    public class AlbumFormModel
    {    
        private static const ARTIST_ERROR : String = "Please enter an artist";
        private static const TITLE_ERROR : String = "Please enter an album title";        
        private static const COMPOSER_ERROR : String = "Please enter a composer";        
        
        /****************
         * Indicates if the album can be edited.  Mostly depends on if there
         * is an album assigned or not
         */
        public var canEdit : Boolean = false;
        
        /*****************
         * Indicates if the composer can be edited.  Mostly depends on if it's
         * set to classical or not.
         */
        public var canEditComposer : Boolean = false;
        
        public var artistError : String = "";
        public var titleError : String = "";
        public var composerError : String = "";

        /******************
         * Indicates if the Cancel button should be enabled or not.  Mostly depends
         * on if there has been changes to the fields of the album or not.
         */
        public var canCancel : Boolean = false;
        
        /******************
         * Indicates if the Apply button should be enabled or not.  Mostly depends
         * on if there has been changes to the values or not, and if the values
         * entered are valid.
         */
        public var canApply : Boolean = false;
        
        private var artistValid : Boolean = false;
        private var titleValid : Boolean = false;
        private var composerValid : Boolean = false;
        
        /***************
         * Current values of the album.  It's a copy of the original so that
         * the changes can be reverted.  Undo/Cancel
         */
        private var _album : AlbumVO;
        
        /****************
         * A reference to the original Album data to be able to updated it when
         * the apply button is pressed.
         */
        private var _albumToUpdate : AlbumVO;        
        
        /*****************
         * The current value of the album being worked on.
         */
        public function get album() : AlbumVO
        {
            return _album;
        }
        public function set album( album : AlbumVO ) : void
        {
            artistError = "";
            titleError = "";
            composerError = "";
            if ( album != null ) 
            {
                _album = album.clone();    
                _albumToUpdate = album;
                canEdit = true;
                canEditComposer = _album.isClassical;
                albumUpdated();
            }
            else
            {
                _album = null;
                _albumToUpdate = null;
                canEdit = false;
                canEditComposer = false;
            }
        }
        
        /********************************
         * This is the message interceptor of the AlbumChangedMessage that is dispatched
         * from the AlbumBrowserModel when the user clicks on a different album from the
         * list.
         * It does several things.  It first checks to see if the current album being
         * worked on has any changes pending.  If it doesn't it updates the current album
         * to the new album and authorizes the message to continue.
         * If the current album has changes pending to be applied, it prompts the user
         * if he/she is willing to loose the changes.  Depending on the answer provided
         * it updates the message accordingly and proceeds to authorize the message to
         * continue, and updates the current album if the user selects to discard the changes.
         */
        [MessageInterceptor(type="com.adobe.ac.messages.SelectedAlbumChangeMessage")]
        public function interceptSelectAlbumChange (processor:MessageProcessor) : void 
        {
            var listener:Function = function (event:CloseEvent) : void 
            {
                var message:SelectedAlbumChangeMessage = processor.message as SelectedAlbumChangeMessage;
                if ( event == null || event.detail == Alert.YES )
                {                
                    message.cancelled = false;
                    album = message.selectedAlbum;
                }
                else
                    message.cancelled = true;
                processor.proceed();
            };
            
            var message:SelectedAlbumChangeMessage = processor.message as SelectedAlbumChangeMessage;
            if (changesToSave())
            {      
                Alert.show("Do you want to abandon your changes?", "Album Browser", 
                    Alert.YES | Alert.NO, null, listener);
             }
             else
             {
                 album = message.selectedAlbum; 
                 processor.proceed();
             }
        }
        
        /*****************************************
         * Called when the artist field is updated an proceeds to validate the data.
         */
        public function updateArtist( artist : String ) : void
        {
            album.artist = artist;
            albumUpdated();
            artistError = setErrorString( artistValid, ARTIST_ERROR );
        }
                
        /*****************************************
         * Called when the title field is updated an proceeds to validate the data.
         */        
        public function updateTitle( title : String ) : void
        {
            album.title = title;
            albumUpdated();
            titleError = setErrorString( titleValid, TITLE_ERROR );
        }
        
        /*****************************************
         * Called when the isClasical checkbox is updated an proceeds to validate the data.
         */        
        public function updateIsClassical( isClassical : Boolean ) : void
        {
            album.isClassical = isClassical;
            canEditComposer = album.isClassical;
            if ( isClassical == false )
            {
                album.composer = "";
                composerValid = true;
                composerError = "";
            }
            albumUpdated();
        }
        
        /*****************************************
         * Called when the composer field is updated an proceeds to validate the data.
         */
        public function updateComposer( composer : String ) : void
        {
            album.composer = composer;
            albumUpdated();
            composerError = setErrorString( composerValid, COMPOSER_ERROR );
        }

        /************************
         * Called when any field is updated, and proceeds to validate the data
         */
        private function albumUpdated() : void
        {
            validateAlbum();
        }
        
        /*************************
         * Performes validation of the data, and sets the flags accordingly.
         */
        private function validateAlbum() : void
        {
            artistValid = album.artist != "";    
            titleValid = album.title != "";
            composerValid = ( album.isClassical == false ) || ( album.isClassical && album.composer != "" );
            canCancel = !album.equals( _albumToUpdate );
            canApply = canCancel && artistValid && titleValid && composerValid;
        }
        
        /****************************
         * Sets the error string for the hint if the field has invalid data
         */
        private function setErrorString( valid : Boolean, errorString : String ) : String
        {
            if ( valid == false )            
                return errorString;
            else            
                return null;
        }
        
        /******************************
         * Updates the original album data to save the changes.
         */
        public function applyChanges() : void
        {
            _albumToUpdate.artist = _album.artist;            
            _albumToUpdate.title = _album.title;
            _albumToUpdate.composer = _album.composer;    
            _albumToUpdate.isClassical = _album.isClassical;
            albumUpdated();
        }
        
        /********************************
         * Reverts the changes.  Copies the original data back again.
         */
        public function cancelChanges() : void
        {
            album = _albumToUpdate.clone();    
        }
        
        /*********************************
         * Indicates if there are changes pending to be applied.
         */
        public function changesToSave() : Boolean
        {
            return canCancel;
        }
    }
}

   Well, the majority of this class has to do with validation, I didn't change that part, and I feel like it can be simplified, but that was not the goal of this example.  In regards to validation Paul wrote: "In a presentation model application the validation logic belongs in the model class. I've often felt Flex Validators are a little cumbersome to use from ActionScript code, so I haven't used them in this example."

   Personaly I think two directional binding would have saved some code, but you can see that you can have pretty cool logic in a fairly simple way.  For example.  If nothing has changed, neither the Apply nor the Cancel buttons are enabled, and it's based on real changes, not some cheap flag that the user has typed something.  If the user returns things as they were, the buttons are disabled again.  I personally really like when applications do that.  Also note that the Apply button is enabled based on more than just changes pending, it also dependent on the form being valid.

  Now what I want you to pay attention to is the [MessageInterceptor] tag in-front of interceptSelectAlbumChange() method.  Message interceptors intercept messages before the handlers actually get a chance to receive the message.  Message interceptors are a great feature of Parsley.

   Recall that in the the AlbumBrowserModel, a SelectedAlbumChangeMessage is dispatched and also handled.  Now in this model, the interceptSelectAlbumChange() will be called before the handler on the AlbumBrowserModel.  What the interceptSelectAlbumChange does is check to see if there are any changes pending.  If there are no changes pending, it sets the current album to the new album in the message, and let's the SelectedAlbumChangeMessage proceed unchanged, which is received by the handler in the AlbumBrowserModel and it does it's thing.  The interesting part is that if there are changes pending, it brings out a popup asking the user if he/she is willing to loose the changes.  If the user is not willing to loose the changes it, proceeds to change the value of a property in the message (in this case the cancelled property, forgive the typo), and then let's the message proceed.  The message is "suspended" for a short period of time.  Message interceptors can also decide to not let the message continue.  But in my particular case I wanted the message to proceed regardless, and just update a flag in the message when necessary.

Author Note:  Szabolcs made an observation in the comments.  I'm using the MessageInterceptor to intercept the message, change the flag and handle the message.  While in this case it works just fine, it's probably not the best idea in a larger project or one with potential for growth.  As Szabolcs notes, MessageInterceptors always get called before the handlers, but I don't have much control of which MessageInterceptor gets called first.  What if I wrote some other Interceptor that decided to cancel the message, but this one got called first?  It would be a better practice to have a MessageHandler in the AlbumFromModel to handle the message independent from the MessageInterceptor.  Although for this case, it would be overkill, it set's things up better for future growth.

   Ok, let's look at the message, here is SelectedAlbumChangeMessage.as:

package com.adobe.ac.messages
{
    import com.adobe.ac.vo.AlbumVO;
   
    /**********************
     *  This is the message that is dispatched when the user selects a new
     * album from the list.
     * 
     *   This class is not part of the original Paul Williams example.  The 
     * purpose of this class is to decouple the AlbumBrowserModel From the
     * AlbumFormModel which are tighly coupled in PaulWilliams example.
     * 
     * @author Arturo Arturo Alvarado
     * 
     */
    public class SelectedAlbumChangeMessage
    {
        private var _previousAlbum:AlbumVO;
        private var _selectedAlbum:AlbumVO;
        private var _cancelled:Boolean = false;
       
        /************************
         *  Constructor 
         * @param previousAlbum The album that was previously selected
         * @param selectedAlbum The album that has been selected and is the new target
         */
        public function SelectedAlbumChangeMessage(previousAlbum:AlbumVO,selectedAlbum:AlbumVO)
        {
            _previousAlbum = previousAlbum;
            _selectedAlbum = selectedAlbum;
        }
       
        /*************************
         * The previously selected Album, current before the selection occours. 
         */
        public function get previousAlbum():AlbumVO
        {
            return _previousAlbum;
        }
       
        /*************************
         * The album that has been selected to become the current album.
         */
        public function get selectedAlbum():AlbumVO
        {
            return _selectedAlbum;
        }

        /*************************
         * Should the change event be cancelled or not.
         */
        public function set cancelled(value:Boolean):void
        {
            _cancelled = value;
        }       
        public function get cancelled():Boolean
        {
            return _cancelled;
        }
    }
}

  Nothing fancy, forgive the typo in the cancelled property named.  Noticed it when I was deep into writing the article.

And here is the AlbumVO.as:

package com.adobe.ac.vo
{   
    /**************************
     *  The is the value object of one Album.  It contains the properties
     * that describe the albums such as title, artist and Composer.
     * It also contains to helper methods to copy the object (clone) and
     * to compare the contests of it.
     * 
     * This is comes from Martin Fowler's post about Presentation Model, which
     * was then ported to Flex by Paul Williams.  I adapted it to show the
     * same example implemented usign the Parsley Framework
     * 
     * Martin Fowler http://martinfowler.com/eaaDev/PresentationModel.html
     * Paul Williams http://blogs.adobe.com/paulw/archives/2007/10/presentation_pa_3.html 
     * Arturo Alvarado http://artinflex.blogspot.com/ 
     */
    [Bindable] 
    public class AlbumVO
    {
        /***********************
         * The tittle name of the album 
         */
        public var title : String = "";
       
        /***********************
         * The Artist name of the album
         */
        public var artist : String = "";
       
        /**********************
         * Flag that indicates 
         */
        public var isClassical : Boolean = false;
       
        /**********************
         *  The name of the composer, in case this is a classical album 
         */       
        public var composer : String = "";
       
        /*******************************
         * This function returns a new copy of this object 
         * @return a complete new copy of this object 
         */
        public function clone() : AlbumVO
        {
            var clone : AlbumVO = new AlbumVO();
            clone.title = title;
            clone.artist = artist;
            clone.composer = composer;
            clone.isClassical = isClassical;
            return clone;
        }
       
        /*********************************
         *  Determines if the Album passed contains the same values as
         * this album 
         * @param source  The album which you wish to compare to this one.
         * @return true if albums are the same.         
         */
        public function equals( source : AlbumVO ) : Boolean
        {
            var result : Boolean = source.title == title;
            result = result && source.artist == artist;
            result = result && source.composer == composer;
            result = result && source.isClassical == isClassical;
            return result;
        }
    }
}

   A somewhat standard Value Object.  Note that it has two "unusual" methods.  The clone() and equals().  Both are used in the AlbumFormModel.  The clone is used to keep a copy of what the data looked like when it was received, and the equals is used to check to see if any values have changed from when it was received.

  And then there is the AlbumDelegate.as:

package com.adobe.ac.delegate
{
    import com.adobe.ac.vo.AlbumVO;
   
    import mx.collections.ArrayCollection;
   
    /**************************
     *  This is a delegate in charged of obtaining the list of albums.
     * Usually delegates contact services to obtain the info.  In this
     * case it simply has the data itself and creates and returns an
     * ArrayCollection of the list of albums with artists and composers.
     * 
     * This is comes from Martin Fowler's post about Presentation Model, which
     * was then ported to Flex by Paul Williams.  I adapted it to show the
     * same example implemented usign the Parsley Framework
     * 
     * Martin Fowler http://martinfowler.com/eaaDev/PresentationModel.html
     * Paul Williams http://blogs.adobe.com/paulw/archives/2007/10/presentation_pa_3.html 
     * Arturo Alvarado http://artinflex.blogspot.com/ 
     */
    public class AlbumDelegate
    {
        /************************
         *  Gets a list of albums 
         * @return an ArrayCollection of albums of type AlbumVO
         */
        public function getAlbums() : ArrayCollection
        {
            var albums : ArrayCollection = new ArrayCollection();
           
            var album : AlbumVO = null;
           
            album = new AlbumVO();
            album.title = "OK Computer";
            album.artist = "Radiohead";
            albums.addItem( album );
           
            album = new AlbumVO();
            album.title = "The Joshua Tree";
            album.artist = "U2";
            albums.addItem( album );
           
            album = new AlbumVO();
            album.title = "Nevermind";
            album.artist = "Nirvana";
            albums.addItem( album );
           
            album = new AlbumVO();
            album.title = "Thriller";
            album.artist = "Michael Jackson";
            albums.addItem( album );
           
            album = new AlbumVO();
            album.title = "Dark Side of the Moon";
            album.artist = "Pink Floyd";           
            albums.addItem( album );
           
            album = new AlbumVO();
            album.title = "Definitely Maybe";
            album.artist = "Oasis";       
            albums.addItem( album );
           
            album = new AlbumVO();
            album.title = "Sgt. Pepper's Lonely Hearts Club Band";
            album.artist = "The Beatles";           
            albums.addItem( album );
           
            album = new AlbumVO();
            album.title = "Like a Prayer";
            album.artist = "Madonna";       
            albums.addItem( album );
           
            album = new AlbumVO();
            album.title = "Appetite For Destruction";
            album.artist = "Guns N' Roses";
            albums.addItem( album );
           
            album = new AlbumVO();
            album.title = "Revolver";
            album.artist = "The Beatles";                       
            albums.addItem( album );
           
            album = new AlbumVO();
            album.title = "Piano Quintet In A Major Opus 81";
            album.artist = "Andreas Haefliger and Takacs Quartet";
            album.composer = "Antonin Dvorak";               
            album.isClassical = true;
            albums.addItem( album );           
           
            album = new AlbumVO();
            album.title = "Lieutenant Kije (suite) Opus 60";
            album.artist = "Lille National Orchestra";
            album.composer = "Sergei Prokofiev";               
            album.isClassical = true;
            albums.addItem( album );           
           
            album = new AlbumVO();
            album.title = "Alessandro Severo - Overture";
            album.artist = "Academy Of Ancient Music";
            album.composer = "George Frideric Handel";               
            album.isClassical = true;
            albums.addItem( album );           
           
            album = new AlbumVO();
            album.title = "Sinfonia In C Minor";
            album.artist = "Czech Chamber Philharmonic Orchestra";
            album.composer = "Josef Barta";               
            album.isClassical = true;
            albums.addItem( album );       
           
            album = new AlbumVO();
            album.title = "Sleeping Beauty - Waltz";
            album.artist = "Kirov Orchestra";
            album.composer = "Peter Ilich Tchaikovsky";               
            album.isClassical = true;
            albums.addItem( album );           
                       
            return albums;
        }
    }
}

   Usually you would be retrieving data from somewhere, this is a "placeholder" for the real class.

   Let's not forget the magic glue, the context config file.  Here is PresentationModelContextConfig.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="com.adobe.ac.model.*">   
<!---   
   This is the presentation model for the root AlbumBrowserView 
-->   
    <model:AlbumBrowserModel />
   
<!---   
   This is the presentation model for the AlbumFormView.  It is set a a dynamic object so
   everytime a new AlbumFormView is instatiated, it will have it's OWN presentation model. 
-->   
    <spicefactory:DynamicObject type="{AlbumFormModel}" />
   
    <mx:Script>
        <![CDATA[
            import com.adobe.ac.model.AlbumFormModel;
        ]]>
    </mx:Script>
</mx:Object>

   As mentioned earlier, the AlbumFormModel is set as a Dynamic Object so that if there are multiple AlbumFormModels, simultaneously or not, state is not preserved among them.  In most cases you will set your Presentation Models as DynamicObjects, regardless if it is a MDI app or not, unless you really don't ever remove the view corresponding to that PM.

  Last, the main application file, where it all starts.  Here is PresentationModelMain.mxml:

<?xml version="1.0" encoding="utf-8"?>
<!--
  This is an example application that demonstrates the Presentation
  model implemented using Parsley.

  This is comes from Martin Fowler's post about Presentation Model, which
  was then ported to Flex by Paul Williams.  I adapted it to show the
  same example implemented usign the Parsley Framework.
  
  If you are new to Parsley I suggest you start with some basic Parsley
  concepts to make it easier to follow this example.
  You can get a quick start at:
  http://artinflex.blogspot.com/2010/09/quick-dive-into-parsley-intro-why.html
    
  Martin Fowler http://martinfowler.com/eaaDev/PresentationModel.html
  Paul Williams http://blogs.adobe.com/paulw/archives/2007/10/presentation_pa_3.html 
  Arturo Alvarado http://artinflex.blogspot.com/ 
-->
<mx:Application 
    xmlns:mx="http://www.adobe.com/2006/mxml" 
    xmlns:view="com.adobe.ac.view.*"
    layout="vertical"
    styleName="application" 
     width="420"
     height="470"
     horizontalScrollPolicy="off"
     verticalScrollPolicy="off"
    xmlns:parsley="http://www.spicefactory.org/parsley" viewSourceURL="srcview/index.html">
    
    <mx:Style source="styles.css"/>
        
<!---    
    ContextBuilder is how you tell Parsley what objects you want it to manage,
    and how you want it to do it.
-->        
    <parsley:ContextBuilder config="{PresentationModelContextConfig}" />
    
<!--- The AlbumBrowser view-->    
    <view:AlbumBrowserView />    
</mx:Application>


   Well this was a fairly long post.  It puts into practice a lot of what is discussed through out the series.  I really do hope you find this post useful. 

   Next is the presentation model implemented with multiple AlbumForms. Click here for the next article.

   Feel free to leave comments, questions and suggestions.

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.