2011-01-11

Parsley Framework tips

As I have been using Parsley lately, thought I'd share a couple of tips. Here goes...

Selectors on result methods
When declaring the result method of your command class, it probably looks like this:

[CommandResult]
public function onResult( ... ):AsyncToken { ... }

That's nice, but if you have several commands classes in your project, Parsley will end up calling all of them once a remote method completes. You need to specify a selector which has the same event code as the associated event class in each command. So the above becomes:

[CommandResult(selector="getContacts")]
public function onResult( ... ):AsyncToken { ... }

Global error handler
Following the same theme, for fault methods, you probably want to have a global one, so that you can display a uniform message when things go wrong. You can accomplish this by creating a class that has a single method with the following declaration:

[GlobalRemoteObjectFaultHandler]
public function handleFault( event:FaultEvent ):void { ... }

And then just declare it in your Parsley configuration file, like so:

<someNS:GlobalAlertHandler />

And finally, since this is not a Parsley feature directly, but a Cairngorm 3 add-on, you will need to download and include in your project the Cairngorm Integration Library, which you can get from here.

CloseEvent and PopUps
If you are using the Cairngorm PopUp Library, then careful when your popup MXML component is throwing the CloseEvent. Throwing this event will cause the popup to close, but if your MXML component is overriding the close handler, you won't get the result you expect!

2011-01-02

Parsley Framework Basics

After studying the Parsley framework lately, I obviously decided to created a mini-application to learn the basics of how the framework operates. And I've decided to share that here. Some concepts like custom Events and Commands will seem familiar if you have used the Cairngorm framework in the past.

Note: Some code omitted for brevity.

Creating View and Presentation Model
Our mini-application will simply display a list of Contacts in a DataGrid that is loaded from a remote source. So needless to say, create an MXML file (Contacts.mxml) that contains a DataGrid control with appropriate settings. The next part is important...no Script blocks! The whole point of the Parsley framework is to put all UI business logic in Presentation Model class, so unit testing is easy.

So the next step is to create the Presentation Model class called ContactsPM. This class should extend EventDispatcher (to be explained later), have a bindable field called contacts of type ArrayList and contain an empty method called search()... like so:

public class ContactsPM extends EventDispatcher {
[Bindable]
public var contacts:ArrayList;

public function search():void {}
}

The last part is to go back into your MXML view file (Contacts.mxml) and add two things. First, declare a bindable instance of the ContactsPM class you just created and also add the [Inject] metadata tag to it, like so:

[Inject]
[Bindable]
public var model:ContactsPM;

The [Inject] metadata tag will be used by Parsley to set an instance of the class when the framework initializes. Secondly, bind the contacts field of the ContactsPM class to the dataProvider of the DataGrid, such that when the contacts are set, the DataGrid will display them. Example is as follows:

<mx:DataGrid dataProvider="{model.contacts}"/>

So far, we have setup our view to use our Presentation Model class to display a list of contacts. How this will actually happen, comes next.

Creating custom Event and Command
The next steps are much like what you would do if you were using the Cairngorm framework. We will dispatch an Event that will in-turn execute a Command to retrieve our contacts. And in the case of Parsley, this is easier cause there are no classes to extend or interfaces to implement (is this better or worse? for you to debate).

First, create the GetContactsEvent class that extends the Flex Event class and simply calls the super constructor with the event name to set. Like so:

public GetContactsEvent extends Event {
public function GetContactsEvent() {
super( "getContacts" );
}
}

Next we need to create the GetContactsCommand class. It must declare two injectable fields, one for the remote object to use and another for the Presentation Model class (ContactsPM) we created earlier. Again, Parsley will set the values on these fields when the Command class is initialized. Example:

[Inject] public var service:RemoteObject;
[Inject] public var model:ContactsPM;

Then we need our command class to declare two methods. First, the execute method that takes our event class as a parameter and calls the remote Java method (notice the [Command] metadata tag that tells parsley which method to call when the event is fired):

[Command]
public function execute( event:GetContactsEvent ):AsyncToken {
return service.getContacts() as AsyncToken;
}

Secondly, a result method that takes an ArrayList as a parameter and sets it on our model so that the view will refresh itself (notice the [CommandResult] metadata tag that tells parsley which method to call once the remote call returns).

[CommandResult]
public function onResult( items:ArrayList ):void {
model.contacts = items;
}

Note: You can also declare a fault method for handling any failures.

Dispatching the Event
Now that we have created certain parts of our application, we need to connect them together, basically how to fire the command when our custom event is fired. Well remember the search() method in our Presentation Model class (ContactsPM) which we left empty earlier? Well time to fill it up with:

public function search():void {
dispatchEvent( new GetContactsEvent() );
}

Next, you need to add some metadata atop the ContactsPM class so that Parsley is aware that it will broadcast this event. This done by adding the following to the top of the class declaration:

[Event(name="GET_CONTACTS", type="demo.commands.GetContactsEvent")]
[ManagedEvents(names="GET_CONTACTS")]
public class ContactsPM extends EventDispatcher {
...
}

The last thing to do is to call the search() method once a button is pressed, as an example. Because the ContactsPM class extends EventDispatcher and the metadata information that was just added, the dispatch() method will use Flex's internal event mechanism to fire the event and then Parsley will take of calling the appropriate method on the appropriate class - in this case the execute method in the command class.

Configuring Parsley
Now the last part of our exercise is to create our Parsley configuration so that it can properly initialize the classes we have created and make our application work.

Creating Parsley configuration file
Like in the world of Spring, we must create a configuration file were all the objects that we need Parsley to manage will be declared. This will be in done in a simple MXML file (ContactsParsleyConfig.mxml):

<Objects xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
xmlns="http://www.spicefactory.org/parsley"
xmlns:dom="demo.domain.*"
xmlns:cmd="demo.commands.*">
<fx:Declarations>
<dom:ContactsPM />

<cmd:GetContactsCommand/>

<mx:RemoteObject id="contactManager" destination="contactManager"/>
</fx:Declarations>
</Objects>

The root tag of this MXML file is the Objects class that is defined by Parsley. Then we declare the Presentation Model class (ContactsPM), the command class (GetContactsCommand) and finally the RemoteObject class that the will be called by the command (in my demo it refers to a Java class).

Notice none of the declared objects have identifiers (id), but for such a small demo, this is not needed. More on that in future blog post :) .

Initializing Parsley
Now only two more changes remain to be done. In our root Application MXML file, we need to tell Parsley to load our configuration file. You do this by using Parsley's ContextBuilder tag as follows:

<s:Application xmlns:sf="http://www.spicefactory.org/parsley">
...
<fx:Declarations>
<sf:ContextBuilder config="ContactsParsleyConfig"/>
</fx:Declarations>
...
</s:Application>

And the final change is to add a single tag to the view (Contacts.mxml) component created earlier to tell it that it is part of the Parsley setup and will need to configured by Parsley when initialized - remember the view has the Presentation Model class as an injectable property:

<mx:VBox xmlns:sf="http://www.spicefactory.org/parsley">
...
<fx:Declarations>
<sf:Configure />
</fx:Declarations>
...
</mx:VBox>

And folks that's it. If anyone needs the actual demo source, please let me know and I will be happy to send them to you. I will be writing more about Parsley in the New Year as I learn more, with some some advanced posts and maybe some tips and tricks.

Until then, Happy New Year to all!