This is part of a series of articles that should get you up and started using
Spicesfactory's Parsley.
This is part 8.
In
Part 3, I showed how to set up Parsley and showed a 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
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 Dynamic Commands and Dynamic Objects.
Parsley’s Dynamic Command & Dynamic Object
Parsley offers two very useful short lived objects: the
<DynamicCommand> and the
<DynamicObject>. Short lived objects are those who aren’t persistent during the life of the context. They are created, used, and destroyed, once or multiple times during the life of their context. You could also call them non-singleton. Let’s take a closer look to each one.
Dynamic Command
Parsley’s Dynamic Command is a built in implementation of the MVC’s Command.
For those not familiar with MVC (Model View Controller), here is a very short summary of what the purpose of Command. The main goal of MVC is to create decoupling, in which you break the application into three main sections. The
View which holds your User Interface elements, the
Model which holds your data, and the
Controller which gets and puts data into the model. The Command is part of the Controller.
The key concept is that the View (the the UI components) really should not care where the data comes from, that being a local database, an ASP page, php, Remote Object, WebService, local object, etc… It should worry only about interacting with the User. This is useful since it keeps your view components cleaner, easier to debug, and it certainly makes it easier to: Reuse the View component, Reuse the data acquiring/saving code, swap the view component, and swap the data acquiring/saving code. So under the MVC concept, the view simply dispatches a message in which it requests the acquiring of data or saving of data, how that is performed is not it’s problem.
Getting and saving data is the Controller’s job. Most MVC implementations have a concept of a Front Controller. The front controller’s job is to receive the messages dispatched (most likely from the View) and to invoke specific Commands to execute the request, that being either: acquiring data from where it should, or saving the data.
When you use Parsley’s
<DynamicCommand>, you do not need to create your own Front Controller, since Parsley and its Context take the job of the Front Controller. Whenever you dispatch a message of a specific type, Parsley will create an instance of the specified Command Class type, and it will call it’s execute method. When done, it destroys that instance automatically, hence it is short-lived.
In Parsley, Command Classes do not need to implement any interface, nor they need to extend any class. They simply have to have at bare minimum a method called execute() that receives one parameter of the associated message type.
At a bare minimum a Command class in Parsley would look like:
package commands
{
public class MyCommand
{
public function execute (message:MyMessage) : void
{
// Do something
}
}
}
In your context config file you must declare the Dynamic Command with the
<DynamicCommand> tag:
<spicefactory:DynamicCommand type="{MyCommand}"
selector="A Specific Request" />
If you pay close attention, there is nothing in the context config file that associates the specific command with the message type. That is because Parsley figures this out automatically. Parsley reads that you wish to create a DynamicCommand of the class type “MyCommand”. It looks into that class for a public method named execute. It then looks at the parameter type that is expected by the execute method and then it associates that parameter type as a message, with that command. So whenever a Parsley managed message is dispatched with that parameter type, Parsley automatically creates an instance of the DynamicCommand type, calls it’s execute method and passes the message as the parameter.
So what is this “selector”. Parsley’s DynamicCommand was designed to be used with or without standard Events. All events have an event type property. What selectors allow you to do, is to have the same message type (class) and invoke different commands depending on the selector.
For example you could have a UserSettingsMessage class that has some properties that would be the same, regardless if you save the user settings or retrieve the user settings. So you could have a “SAVE User Settings” selector, and a “GET User Settings” selector that invoke different commands with the same Message type.
In Parsley the selector does not have to be a string type, nor it actually has to be called type. That’s just the default. As long as it returns a value that can be compared by value and not by reference it can be called anything. So selectors can be Number, String and Integers, but I would stick to Strings and Integers. In your message class, you just use the
[Selector] tag in front of the property you wish to be the selector and Parsley does the rest.
Since Parsley’s DyanmicCommand is a managed object, it can participate in all of Parsley’s features, including Messaging and Injection, which takes us to Dynamic Objects
Dynamic Object
Parsley’s Dynamic Object aren’t associated in any way with Dynamic Commands, they simply work well together. Dynamic Objects can be any non-view classes that you wish to have Parsley manage, but you do not want them to be persistant. A standard managed object of Parsley is instantiated when you inject its class type into any other Parsley managed object or view, but that instance remains persistent, if you inject the same class within the same context the same instance is injected in each injection, basically being shared. This is also called singleton, as it’s a single instance.
Dynamic Objects are not persistent. Each injection of that class type creates a whole new instance, something akin of using new() on each injection.
You might be wondering, why in the world would you just not simply use new() then? Well, couple of reasons. First; since it is a Parsley Managed object, that class can participate in any of Parsley features, so it can itself have injections and do Messaging, receive and send. Second; when using Interfaces, it is easy to swap out the Real class in all places where you Inject that Interface. You just change it in the context config file once.
Ok, enough background info, let’s look at how this actually works.
The Examples
I want to set some things straight:
First: This is NOT a complete MVC implementation. It is focused primarily on the Controller side. The Model and View are extremely simple to keep the focus on the Controller and the Command. I will later show a complete MVC example with Presentation Model.
Second: There is no one single way to implement Controllers and Commands the "
right way". The right way depends on many things. Don’t forget that the goal is to make things simpler by decoupling, but don’t forget to keep things simple.
Generally speaking, Commands usually invoke services, as the command itself is not the service. The Service will be either a Remote Object call, a WebService, or a HTTPService for example. You can instantiate new services within the command or Inject Services to the Command. I particularly like Injecting services into the command as it makes it simpler to swap out the service, with little or no change to the Command. In Cairngorm, for example, Commands usually invoke “
Delegates” which have the services in them, it’s a little extra step that you might consider.
Another decision to make is who should handle the result from the request of the service. The Service itself or the Command? Both have their pros and cons. If you wish to have the Service handle the result, then you must extend the service and create your own class that has the result handler in it, or use the
Delegate concept. If the Command handles the result, then the Command gets tied to that service implementation.
There is no one perfect answer. You should consider your implementation on the following: How likely are you to use this application with a different server technology, or update your communication protocol with the server. If you think that there is a fair chance of you implementing a different server technology or move from XML to JSON or AMF, or something of your own, I would suggest having the Service handle the result.
Well, just to keep everyone happy, I’ve implemented both here. Two examples of the same thing, one with the Service handling the result, and the other with the Command handling the result.
Ok, this is a very simple RSS reader, and I really mean simple, the view and the model are as simple as they can get, and it was only tested with the RSS feed from this blog, but it should work with any other blogspot RSS feed. For those unfamiliar with what an RSS feed is, it’s a feed used to publish frequently updated work. More info on Wikipedia (
http://en.wikipedia.org/wiki/RSS). RSS feeds are returned in XML, and I did almost no parsing of the XML, just the bare minimum, as the focus here is not to create a full RSS reader, but look on how to implement a
<DynamicCommand>. In the real world, I would very rarely store XML data in my model. One of the great things about Flex is that it lets you prototype so easily.
The Service handling the result example:
Here is the running version of the Service handling the result. View Source is enabled.
NOTE: I had to make a quick change to the examples. After I posted the example I realized that blogger's crossdomain.xml file doesn't allow for swf's hosted on other domains to read the RSS feed. I resolved the issue by reading the RSS feed through "Feed burner" (
http://www.feedburner.com).
Now let take a look at the structure of the project.
This project has the basic structure of an MVC project, where you have models, views, and the controller all independent from each other.
We have one Command which is the GetRssFeedCommand class.
We have one Message which is the GetRSSFeedMessage class. When we dispatch this message, the GetRssFeedCommand should be instantiated, and have its execute method called.
Our Model is very simple, it’s the RssFeedModel. The model will store the result from the service invoked by the command. The view will be bound to a property of our model, so whenever the model gets data, so will our view.
In regards to the service, there are two classes. One is an Interface, called IRssService. Interfaces are not requiered, it simply makes it much easier to swap out the real service class if we wish to. For example we could have a mockup Service class that fakes getting data and we could quickly swap it latter. The real service class is the RssXMLHttpService class. That is the class that connects and gets the data.
The view, well, I stuck the List component in the main component called DynamicCommandExample, so the main file is the view. I have simply one item renderer for the List which is called RssFeedItemRenderer.
Of course we also have our Context configuration object, which is called DynamicCommandContextConfig.
Now let’s go into the details.
Let's start of with the Context config file which actually makes this work. Here is
DynamicCommandContextConfig.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:models="models.*"
xmlns:services="services.*" xmlns:local="*">
<!---
Our simple Rss Model that holds the results
-->
<models:RssFeedModel />
<!---
Here is the Service that we ask Parsley to use. Since this service implements
IRssService, Parsley figures out that it should inject this service whenever
an Injection request for IRssService is made.
Notice that I've declared the Service as a DynamicObject. This means that a new
instance will be created every time it is injected, and when the reference to this
instace is finished, the old instance is removed. Type is the name of the class
you wish to have created.
Alternatively I could have declared it as a "Singleton" in the sense that once
created it remains during the life of this context. When using Soap WebServices
I always use the singleton aproach so that the wsdl only downloads once.
Using the singleton aproach the declaration should be:
<services:RssXMLHttpService />
-->
<spicefactory:DynamicObject type="{RssXMLHttpService}" />
<!---
This is the declaration for the Dynamic Command. DynamicCommands are created automatically
when a message of the type of the parameter received by the execute method is dispatched.
type is the name of the class that you wish to have invoked. Selector is the specific
identifier so that you can have more than one message of the same message class dispatched
and the selector let's you select wich dynamic command is associated with it. I use
public static const so that it is obvious wich message class is associated, although
it is obtained by Parsley from the parameter of the execute method.
You can also specify the name of the execute method, if it's not execute, but I would
discourage you to do so. Execute is standard across all MVC frameworks.
-->
<spicefactory:DynamicCommand type="{GetRssFeedCommand}" selector="{GetRSSFeedMessage.REQUEST_FEED}" />
<mx:Script>
<![CDATA[
/* The imports are necesary for the { } class usage */
import messages.GetRSSFeedMessage;
import commands.GetRssFeedCommand;
import services.RssXMLHttpService;
]]>
</mx:Script>
</mx:Object>
Although not obvious in the declaration (it is in the comment), when the GetRSSFeedMessage is dispatched that’s when GetRssFeedCommand is instatiated. This is one thing I don’t like about Parsley’s implementation of the DynamicCommand. I feel that it is really cool that it figures it out automatically from the parameter type expected in the execute method, but I have to go dig for it if I don’t remember it. That’s why I try to make it obvious in the selector attribute, but keep in mind that they don’t actually have to be related, to Parsley the specified selector in the
<DynamicCommand> declaration is just a value to look for.
Now let’s take a look at the message that will be dispatched. Here is
GetRSSFeedMessage.as:
package messages
{
/********************************************************
* This is the message that is instatiated and dispatched.
* It is meant to request an RSS Feed.
* Parsley uses primarely the class type to select what Command
* to execute, but it recognizes that you could also have the same
* class and wish to invoke different Commands depending on some
* values of this class. So it also uses a selector property of this class.
* Somewhat similar to the Event type selector, but without the same limitations.
*
* For more Parsley Examples visit artinflex.blogspot.com
*/
public class GetRSSFeedMessage
{
/*************************
* This will be the selector type. Notice that it doesn't have
* to be a String, but use types that are compared by value by default.
*/
public static const REQUEST_FEED:int = 1;
/*****
* Internal type holder
*/
private var _type:int;
/***********************
* Constructor that receives the type
* @param requestedType the type selector for this message
*/
public function GetRSSFeedMessage(requestedType:int)
{
_type = requestedType;
}
/***********************
* In parsley the Selector can be any property name. You must use
* types that are easily compared by value and not by reference.
* You simply tag the property with the Selector tag to indicate to
* Parsley that this is the property you wish to use as selector.
*/
[Selector]
public function get type():int
{
return _type;
}
}
}
In this example I used the property name
type as the selector, which is fairly standard, but you can name it whatever you want. Just make sure you use the
[Selector] tag to let Parsley know which property you want to use. Also here is the public static const to help me identify the message class in the context config file mentioned earlier.
Now, let’s look at the simple model, this is where the data will be stored. Here is
RssFeedModel.as:
package models
{
/*****************************************
* This is a very simple model that holds an XMLList to hold the
* result from the RSS Feed service request. It will be bound by a
* mx::List to get the values.
*
* For more Flex and Parsley examples visit: artinflex.blogspot.com
*/
public class RssFeedModel
{
/************************************
* The list that holds the rss feed. It is bindable
*/
[Bindable]
public var feedXML:XMLList;
}
}
Nothing special in our model. It simply has a property called
feedXML that will hold the data. I would usually not hold data as an XMLList but I wanted to keep this simple.
Let’s move on to the Command. Here is
GetRssFeedCommand.as:
package commands
{
import messages.GetRSSFeedMessage;
import mx.rpc.AsyncToken;
import services.IRssService;
/*********************************************************
* This is a short lived dynamic command class. In parsley Commands are
* normaly created when a message is dispatched of it's expecting type.
* Once created the execute method is called and passed the argument.
* This command injects a service interface and proceeds to call
* the service methods.
* Once the command completes it is automatically destroyed unless specifically
* told not to.
*
* For more Parsley and Flex Examples visit artinflex.blogspot.com
*/
public class GetRssFeedCommand
{
/*********************************
* The service is injected through an Interface. This makes it easy
* to swap out the service with a different class. Parsley figures
* automatically out through reflection wich of it's manage object
* should it inject by determining which classes implement this
* interface.
*/
[Inject]
public var service:IRssService;
/*********************************
* Once Parsley creates this command object and performs it's injections,
* it proceeds to call the execute method. You can tell Parsley to call
* a different method, but this is discouraged. The execute method
* can return an AsyncObject.
* Parsley determines which command to execute by the object
* type of the parameter of this execute method.
*
* @param message is the message sent. It's type is used to determine
* which command to execute.
*/
public function execute (message:GetRSSFeedMessage) : void
{
service.readRssXMLFeed("http://feeds.feedburner.com/ArtsFlexNotes");
}
}
}
This command class is actually pretty simple, and most of them will usually be simple. In this version of the command, handling the result is taken care of by the service. Commands may have more complex logic. For example, they might check if the model already has this data, and not go to a service to get it again (if you know it won’t change), they might also decide where they will go get the data, etc… Also Commands are not just about getting data, they also save data, process data, etc… For example you might have complex and dynamic
Validators implemented as commands.
Now this command is relying on a service. Since the command is managed by Parsley it can Inject, as show and it Injects the IRssService, which Parsley fills in with RssXMLHttpService because it knows that RssXMLHttpService implements IRssService through its inspection/reflection process.
First Let’s take a look at the Interface
IRssService.as:
package services
{
import mx.rpc.AsyncToken;
/*********************************************
* This interface is used to read RSS Web Services
* Using Interfaces makes it easy to switch the actual
* Service classes.
*/
public interface IRssService
{
/**********************************************************
* This function will send a request to read an rss xml feed and
* store the result in the feedModel
* @param rssURL the url of where the RSS feed can be found.
* @return The call's AsyncToken in case the command wants
* to add it's own handlers
*/
function readRssXMLFeed(rssURL:String):AsyncToken;
}
}
The interface just defines the methods that the Command will need to access. In this case it has a method named readRssXMLFeed() that expects the URL of the feed. Notice that the definition specifies that the service returns an AsyncToken. In this implementation of the Command, the AsyncToken returned is being ignored, but on the following example it will be used.
So let’s look at the real service itself. Here is
RssXMLHttpService.as:
package services
{
import models.RssFeedModel;
import mx.controls.Alert;
import mx.rpc.AsyncToken;
import mx.rpc.IResponder;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import mx.rpc.http.mxml.HTTPService;
/***********************************************************
* This is a simple RSS consumer service class that will read an
* RSS Feed. I used the MXML version simply because it has
* the showBusyCursor property that I like. It displays a hour
* glass while it submiting the request.
* @author Arturo Alvarado
* For more Flex and Parsley examples visit artinflex.blogspot.com
*/
public class RssXMLHttpService extends HTTPService implements IRssService,IResponder
{
/***********************************
* This is the model that we will place the feed that we will receive.
* The view will get the data from the model
*/
[Inject]
public var modelRssFeed:RssFeedModel;
/*********************************************
* Constructor
*/
public function RssXMLHttpService(rootURL:String=null, destination:String=null)
{
super(rootURL, destination);
showBusyCursor = true;
}
/****************************************
* This function will send a request to read an rss xml feed and
* store the result in the feedModel
* @param rssURL the url of where the RSS feed can be found.
* @return The call's AsyncToken in case the command wants
* to add it's own handlers
*/
public function readRssXMLFeed( rssURL:String):AsyncToken
{
this.url = rssURL;
this.resultFormat ="e4x"
var call:AsyncToken = this.send();
call.addResponder( this);
return call;
}
/*************************************
* This is the result handler it takes the result from the http
* request and stores and XMLList of all the nodes taged with <item></item>
* into the model
* @param data is the result object that we will make sure it's a Result Event
*/
public function result(data:Object):void
{
if (data is ResultEvent)
{
var event:ResultEvent = data as ResultEvent;
modelRssFeed.feedXML = XML( event.result )..item
}
}
/******************************************
* Fault handler in case the request failed.
* @param info object that contains the Fault data.
*/
public function fault(info:Object): void
{
var event:FaultEvent = info as FaultEvent;
Alert.show(event.fault.message,event.fault.faultDetail);
}
}
}
This service is a Parsley managed object. Recall from the Context configuration file that we specified it to be a Dynamic Object, which means that a new instance will be created, used and destroyed each time a Command Injects it. This has the benefit of using fewer resources while the service isn’t being used. When using Soap Web Services you might wish to reconsider this approach, and leave either the service or something from it, persistent, that way you don’t have the service request a
wsdl file every time you wish to make a request.
This service handles the result and fault. Since it does implement IResponder, it can assign itself as a Responder to the AsyncToken returned. I like this approach better than the addEventListener() / removeEventListener() approach for handling results and faults.
In this case, being managed by Parsley means that it can Inject the model, which it does, and store the result in the model, which it does.
Normally, I would have a defined a ValueObject
(in case you are not familiar with the term ValueObject check out: http://blog.tsclausing.com/post/10) with the fields I’m interested in. I would create an instance of that value object, assign the values obtained from the Feed’s XML to that instance and push that instance to an Array, ArrayList, or ArrayCollection in the Model..
So that’s the backend of our app. Let’s look at the View. The view is made up by two components, the Main app, called DynamicCommandExample, and by an Item Renderer named RssFeedItemRenderer.
Let’s take a look at
DynamicCommandExample.mxml:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
xmlns:parsley="http://www.spicefactory.org/parsley" viewSourceURL="srcview/index.html">
<!---
ContextBuilder is how you tell parsley to manage objects that are not Views.
In this case I'm instructing Parsley to load the DynamicCommandContextConfig file which
has the "list" of objects you wish to have managed, including the definition of
the dynamic command.
-->
<parsley:ContextBuilder config="{DynamicCommandContextConfig}" />
<!---
I want Parsley to manage this view, as it will hold the mx:List that will display
the feed and I wish to pass the models data provider, though Injection. Normaly
I would not do this in the main app, but the focus of this example is the Dynamic Command
and really wanted to keep the view part very simple.
-->
<parsley:Configure />
<!---
The button that requests the feed.
-->
<mx:Button x="10" y="10" label="Retrieve RSS Feed" click="requestFeedButtonClickHandler()"/>
<!---
The list that displays the feed result. This does not have to be on the same view
as the requesting part. It binds to a property of the model.
-->
<mx:List x="20" y="40" width="389" height="321"
dataProvider="{modelRssFeed.feedXML}"
itemRenderer="views.RssFeedItemRenderer"></mx:List>
<mx:Script>
<![CDATA[
import models.RssFeedModel;
import commands.GetRssFeedCommand;
import messages.GetRSSFeedMessage;
/*********************************************************
* The dispatcher function injected so that we can request the
* feed and Invoke the command by dispatching a Message.
*/
[MessageDispatcher]
public var dispatcher:Function;
/*******************************************************
* The model that we will use to set the data provider of the list.
* The model receives the data from the service invoke by the command.
*/
[Inject] [Bindable]
public var modelRssFeed:RssFeedModel;
/*******************************************************
* This function get's executed when the button is clicked to
* request the feed. Notice that there is no reference to the command
* nor the model. This view has no reason to know who or how will get the
* data. Is it from a local database, is it from a feed, is it an XML local
* file. That is all irrelevant to this view, all it does is say it wants
* the data and dispatches a message to request it. Parsley takes over from
* there.
*/
public function requestFeedButtonClickHandler():void
{
dispatcher(new GetRSSFeedMessage(GetRSSFeedMessage.REQUEST_FEED));
}
]]>
</mx:Script>
</mx:Application>
This view is doing more than usual for being the Main file, but it is fine for this example. This view holds the Context, it is also configured for this context so that it can participate, so it has a
<ContextBuilder> tag and a
<Configure> tag.
The view has a button that when pressed it simply dispatches a new
GetRSSFeedMessage of the
REQUEST_FEED type. As you can see, it doesn’t know anything about the Command or the Service. It just wants data and it dispatches a messages asking for it, no matter where It comes from.
As this is a very simple view, I have a list that displays the results that is bound directly to the shared model’s
feedXML property. It is notified of data being received through standard Flex binding. While this is ok in small projects or prototypes, I would suggest using Parsley Messaging to notify the view of the results. One inherit problem of using this approach is that the view will only get notified if data was changed, but if the request brought an empty result, the view would not know about it and the user could think that the application hung. At a later time I’ll show a “better” way of doing this.
Finally, this list uses a custom item renderer to display the data. Here is
RssFeedItemRenderer.mxml:
<?xml version="1.0" encoding="utf-8"?>
<!---
This is the Item Renderer for the List. It isn't designed to look good.
It is designed to be simple to view the code. Normally, I would assign
this to a VO and bind the properties to the VO so I can have proper
compiler checking, but for this example it bound to the xml. When run
under debug mode you will get warnings that proper binding was not achieved
since you can’t bind to XML objects.
For more Flex & Parsley examples visit artinflex.blogspot.com
-->
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml"
verticalGap="0"
horizontalScrollPolicy="off"
verticalScrollPolicy="off"
>
<mx:Script>
<![CDATA[
/***************************************************
* Using namespace makes it a bit easier to parse xml
* The updated field of the xml uses the atom namespace
*/
namespace atom = "http://www.w3.org/2005/Atom";
use namespace atom;
/***************************************************
* This function should really be used to set a VO
* so that you can bind to known properties. Here
* I simply used it to help me view the contents of
* the xml rss feed to find out the properties.
* Data tipically receives an Object that you should
* assing it to a local var of a know Value Object
* @param value The data passed by the dataprovider
* it is one record of the list.
*/
override public function set data(value:Object):void
{
super.data = value;
}
]]>
</mx:Script>
<mx:Label
width="100%"
fontWeight="bold"
text="{data.title }"
selectable="false" />
<mx:Label
width="100%"
text="Publish Date: {data.pubDate}"
selectable="false" />
<mx:Label
width="100%"
text="Author: {data.author}"
selectable="false" />
<mx:Label
width="100%"
text="Last Updated: {data.updated}"
selectable="false" />
<mx:Label
width="100%"
text="Link: {data.link}"
selectable="true" />
</mx:VBox>
When it comes to the item renderer, there isn’t anything really special other than the namespace declaration. As mentioned earlier, normally I would not be passing XML, much less raw XML to an item renderer, just keeping it simple.
Well that’s it, now let’s look at the other version.
The Command handling the result example:
Here is the running version of the Command handling the result. View Source is enabled.
Here is the Structure of this version of the example:
As you can see, there isn’t any apparent difference between these two. In fact, there really isn’t a whole lot. The overall structure is basically the same, although using the interface for the service is a bit less useful. This is because swapping the service might have an effect on the content of the result, which means that your result handler in the Command must be able to handle the new result format. You could write code in the command that could check the result, figure out the format and process it accordingly, but I would honestly only consider that in the case that you really were planning on swapping the Service in a regular fashion or at runtime.
The only two classes that changed are RssXMLHttpService and GetRssFeedCommand, everything else is exactly the same.
I’ll start off with the service. Here is
RssXMLHttpService.as:
package services
{
import mx.rpc.AsyncToken;
import mx.rpc.http.mxml.HTTPService;
/***********************************************************
* This is a simple RSS consumer service class that will read an
* RSS Feed. I used the MXML version simply because it has
* the showBusyCursor property that I like. It displays a hour
* glass while it submiting the request.
* @author Arturo Alvarado
* For more Flex and Parsley examples visit artinflex.blogspot.com
*/
public class RssXMLHttpService extends HTTPService implements IRssService
{
/*********************************************
* Constructor
*/
public function RssXMLHttpService(rootURL:String=null, destination:String=null)
{
super(rootURL, destination);
showBusyCursor = true;
}
/****************************************
* This function will send a request to read an rss xml feed and
* store the result in the feedModel
* @param rssURL the url of where the RSS feed can be found.
* @return The call's AsyncToken in case the command wants
* to add it's own handlers
*/
public function readRssXMLFeed( rssURL:String):AsyncToken
{
this.url = rssURL;
this.resultFormat ="e4x"
var call:AsyncToken = this.send();
return call;
}
}
}
The difference between this one and the previous one is that this one doesn’t handle the result or fault. It simply returns the AsyncToken to the caller.
It also no longer needs to Inject the Model into it. Since Injection is no longer needed, there is also no need to have this class to be managed by Parsley. In this example it still is, but there really isn’t a need for it anymore. The only benefit as it stands, it’s the ease of swapping it, but don’t forget that it could affect your Command.
Now let’s look at the Command, here is:
GetRssFeedCommand.as:
package commands
{
import messages.GetRSSFeedMessage;
import models.RssFeedModel;
import mx.controls.Alert;
import mx.rpc.AsyncToken;
import mx.rpc.events.FaultEvent;
import services.IRssService;
/*********************************************************
* This is a short lived dynamic command class. In parsley Commands are
* normaly created when a message is dispatched of it's expecting type.
* Once created the execute method is called and passed the argument.
* This command injects a service interface and proceeds to call
* the service methods.
* Once the command completes it is automatically destroyed unless specifically
* told not to.
*
* For more Parsley and Flex Examples visit artinflex.blogspot.com
*/
public class GetRssFeedCommand
{
/*********************************
* The service is injected through an Interface. This makes it easy
* to swap out the service with a different class. Parsley figures
* automatically out through reflection wich of it's manage object
* should it inject by determining which classes implement this
* interface.
*/
[Inject]
public var service:IRssService;
/***********************************
* This is the model that we will place the feed that we will receive.
* The view will get the data from the model
*/
[Inject]
public var modelRssFeed:RssFeedModel;
/*********************************
* Once Parsley creates this command object and performs it's injections,
* it proceeds to call the execute method. You can tell Parsley to call
* a different method, but this is discouraged. The execute method
* can return an AsyncObject.
* Parsley determines which command to execute by the object
* type of the parameter of this execute method.
*
* @param message is the message sent. It's type is used to determine
* which command to execute.
*/
public function execute (message:GetRSSFeedMessage) : AsyncToken
{
return service.readRssXMLFeed("http://feeds.feedburner.com/ArtsFlexNotes");
}
/*************************************
* This is the result handler it takes the result from the http
* request and stores and XMLList of all the nodes taged with <item></item>
* into the model
* @param data is the result object that we will make sure it's a Result Event
*/
public function result(data:Object):void
{
if (data is XML)
{
modelRssFeed.feedXML = XML( data )..item
}
}
/******************************************
* Fault handler in case the request failed.
* @param info object that contains the Fault data.
*/
public function error (info:Object): void
{
var event:FaultEvent = info as FaultEvent;
Alert.show(event.fault.message,event.fault.faultDetail);
}
}
}
Well, the obvious change is that it must handle the result and fault from the service as it is no longer handled by the service and of course you must inject the Model. But look closely, there are a few more differences.
First off, the execute method no longer returns a
null, it is actually returning an
AsyncToken, the same AsyncToken returned from the Service. Why in the world would I want to do that? If we didn’t invoke the
execute() method directly, what is actually receiving the returned AsyncToken? Well, that’s a nice little trick up Parsley’s sleeve. Notice that I didn’t add any responders, nor I added any event listeners? How does the
result() method get called?
Well, Parsley itself is invoking the execute() method of the command and when it receives an AsynToken it goes ahead and looks in your Command to see if you have a method called
result() that is expecting an Object, and it also checks to see if you have a method named
error() that is also expecting an Object, and calls them accodingly. Don’t ask me why Jens (creator of Parsley) decided not to go with the standard fault(), and used error(), no clue. But if you really want to use anything else and not error(), you can tell Parsley in the Context Config file to use a different name.
Now, also notice that the result handler isn’t exactly the same as the result handler in Service from the previous version of this example. Parsley actually does some cleaning up for you, and it doesn’t actually send a ResultEvent. It actually sends the ResultEvent’s result property, something like:
"ResultEvent(event).result". This way you don’t actually have to typecast to ResultEvent since that’s actually not what you get as the parameter to your handler, you get the result of the ResultEvent. Hope I didn’t make something that is simple, confusing.
The
error() handler is basically the same as a standard fault handler, with the exception of the name change.
Now I mentioned that you can change the name of the methods invoked automatically in the DynamicCommand by Parsley. It’s actually fairly simple. In your Context config file, where you declare your DynamicCommand you just specify it. Here is an example:
<DynamicCommand
type="{SaveUserCommand}"
selector="save"
execute="executeSaveUser"
result="handleSaveUserResult"
error="handleFault"
/>
Parsley also offers a more unique (not very standard) way of doing Commands. It is called the "Asynchronous Command Methods". It is based on using tags in front of methods to act as Commands. The cool factor is that you don’t have to create a Command class. You can place these tags in any Parsley managed object. I particularly like the Short-lived Dynamic Command approach better, simply because it is much more organized. Having Command handlers anywhere, can lead towards massive confusion and a complete pain to follow the code.
You can read more about the Parsley’s Commands in section
6.9 Asynchronous Command Methods and in section
6.10 Short-lived Command Objects in the
docs.
I’m sure some might wonder which approach is better, the first or second demonstrated here. “Better” is such a subjective word. What is better for me is not necessarily better for you.
I like the first approach over the second one. As neat as the automatic call to the result handler in the Command might be, it can be confusing to someone familiar with Flex and not familiar with Parsley’s DynamicCommand. The boilerplate code saving is truly minimal.
I really like the ability to change my server side technology with little to almost no impact on the client side. Over the years, we have improved our communication between the client and server side, and I really like having things organized in a way that it makes it really simple to do so.
Well, I certainly hope that this was useful. It’s a great feature available in Parsley.
Next -> Parsley's Automatic Component Wiring examples.