Subscribe: Posts / Comments / Email


RSS

Chapter 4

Chapter 4

Cairngorm Recipes

Recipe 4.1 – Learning about the components of Cairngorm

Problem: You want to start using a framework for you application before it gets to big, thus making it easier to maintain. But you do not know where to start, and you heard a lot of good things about Adobe’s Cairngorm Framework.

Solution: Lets take a minute to understand the components of Cairngorm. The Cairngorm is made up of numerous classes/packages that help the developer separate all logic of the application into maintainable classes of code. But when creating these classes it is good to understand what each class is suppose to do, and how it works. Cairngorm involves a ModelLocator, a ServiceLocator, a FrontController, a Delegate, a Command, an Event; the view is made up of your components.

Discussion:

The ModelLocator holds the data that is being used through out the application, it acts as a data storage bank for your application, any data is required to live inside of your application, should be placed inside of your appliations ModelLocator.

The ServiceLocator holds the locations of all server side services that your application will be accessing such as HTTP, Web Services, Flash Remoting, etc. Placing all of your services in one central area will be very manageable and extendable for the future.

The Delegate is responsible for calling the actual server side methods on the server. It accesses those services by using the ServiceLocator to locate the server, and itself to actually invoke those methods.

The FrontController is responsible for capturing Cairngorm Events that are dispatched throughout the application, such as clicking of a button, initializing of a component, response from user interaction, etc. The FrontController then maps the captured event to the proper Command.

The Command is responsible processing the event by running the Command class execute() method, this method is an ICommand interface method, which the Command implements. The Command is also responsible for updating the ModelLocator, as well as invoking a server side service with the help of a Delegate.

The Value Object represents an entity of something, the Value Object is nothing more than a representation of a item, or data structure, it just holds the attributes of data in which you are representing.

Last is the View, the view of your application in which will be presented to the user for interaction, the view is responsible for dispatching user or system-generated events. It also binds to the ModelLocator for data representation.

Refresher
• ModelLocator holds data
• ServiceLocator holds service locations
• Delegate holds service methods
• FrontController captures events and maps to proper Commands
• Commands carry out the event, by updating the ModelLocator and/or calling services defined inside the Delegate
• Views dispatch application wide events
• Views watch data stored inside the ModelLocator
• Views are visual components (buttons, panels, data grids, etc.)
• Views can contain child views and components
• Even <mx:Application/> and <mx:WindowedApplication/> is a view

Benefits of using Cairngorm
• Allows designers, developers and data-service developers to work together seamliessly
• Best used for any sized application
• Provides also easier maintenance, easier debugging, feature changes, and enhancements

Back to top

 

Recipe 4.2 – What’s a Value Object?

Problem: I read about the value object in the documentation, and have come across articles that use a “value object”, but I don’t know exactly what it is.

Solution: We will create a value object and go over what it does and its purpose in the application.

Value objects or VOs typically look like this below. Create a new file called ContactVO.as

ContactVO.as:

package com.jonniespratley.flexcontacts.vo
{
import com.adobe.cairngorm.vo.IValueObject;

[RemoteClass(alias="com.jonniespratley.flexcontacts.vo.ContactVO")]

[Bindable]
public class ContactVO implements IValueObject
{
   	public var id:int;
		public var name:String;
		public var address:String;
		public var city:String;
		public var state:String;
		public var country:String;
		public var email:String;
		public var phone:String;
		public var zip:String;
		public var date:String;

		public function ContactVO( obj:Object = null )
		{
			if ( obj != null )
			{
				this.id = obj["id"];
				this.name = obj["name"];
				this.address = obj["address"];
				this.city = obj["city"];
				this.state = obj["state"];
				this.country = obj["country"];
				this.email = obj["email"];
				this.phone = obj["phone"];
				this.zip = obj["zip"];
				this.date = obj["date"];
			}
		}
	}
}

Notice: Your Value Object does not have to implement IValueObject, This is only done for the use of the Eclipse Cairngorm Explorer plug-in by Enterprise IDE.

Discussion: When developing Flex applications it is best to create classes that represent the objects being represented or accessed through out your application, if you were displaying pictures from a art gallery, you would create a class named PhotoVO.as with public variables that represent the attributes of that photo such as height, width, filename, etc.

Value Objects are used to create a layer of data objects that can be transferred between components and server side tiers, instead of generic objects, arrays, etc.

When you use Value Objects to represent data you are allowing a stricter typing on the client, this is really great when working in Flex Builder because any references to properties inside of that value object that do not exist, or types that do not correspond with one another, the compiler will throw a error, letting the developer know exactly where the problem lies. You are setting a standard for the specific enitity you are representing.

Back to top

Recipe 4.3 – What’s a Model Locator?

Problem: I have seen this Model Locator used and have heard about what it does, but still I am not seeing the point of using a “ModelLocator”.

Solution: To better familiar ourselves with the Model Locator we are going to create a “ModelLocator” and discuss what it does and its purpose.

Create a new file named FlexContactsModelLocator.as

FlexContactsModelLocator.as:

package com.jonniespratley.flexcontacts.model
{
    import com.adobe.cairngorm.model.IModelLocator;
    import com.jonniespratley.flexcontacts.vo.ContactVO;

    import mx.collections.ArrayCollection;

    [Bindable]
    public final class FlexContactsModelLocator implements IModelLocator
    {
        private static var instance:FlexContactsModelLocator;

        public function FlexContactsModelLocator()
        {
            if( instance != null ) {
                throw new Error( "Error: FlexContactsModelLocator can only be instantiated via getInstance() method!" );
            }
            FlexContactsModelLocator.instance = this;
        }

        public static function getInstance():FlexContactsModelLocator
        {
            if( instance == null ) {
                instance = new FlexContactsModelLocator();
            }
            return instance;
        }

        public var contactsCollection:ArrayCollection;
        public var selectedContact:ContactVO;

        public var workflowState:uint = 0;
        public static const LOGIN_SCREEN:uint = 0;
        public static const MAIN_SCREEN:uint = 1;
    }
}

Above we have a few variables and lets look at them, first variable is instance which is type FlexContactsModelLocator. It is the same type as the class because we are using this variable to create a singleton for this class.

If we try to create a variable somewhere inside our application it will throw an error because the only way we can access the models data is by getting an instance of our model locator. The getInstance() function is returning an instance of our class.

Discussion:

To access data that is stored inside of the model you first create a Bindable private variable inside of your view or any class that needs to access the data, set the variable type to the name of your ModelLocator class (FlexContactsModelLocator.as).

Then you set that variable equal (=) to FlexContactsModelLocator.getInstance(), creating an instance of your model in which allows access to any part of that data bank.

Creating a variable type FlexContactsModelLocator inside view:

[Bindable] private var model:FlexContactsModelLocator = FlexContactsModelLocator.getInstance();

Accessing data from the model variable:

(<)mx:DataGrid id="dg_contacts" dataProvider="{ model.contactsCollection }"/>

Controlling applications state via your model:

(<)mx:ViewStack width="100%" height="100%" selectedIndex="{ model.workflowState }">

Back to top

Recipe 4.4 – What’s a Event?

Problem: Ok I know what an event is, but how does it become useful in my application? What does this event do and why does it do it?

Solution: Well to get the hang of how events work lets go ahead and create one, then we can discuss what it does what it does and how it does it. Create a new file called GetContactsEvent.as.

GetContactsEvent.as:

package com.jonniespratley.flexcontacts.events
{
    import com.adobe.cairngorm.control.CairngormEvent;

    import flash.events.Event;

    public final class GetContactsEvent extends CairngormEvent
    {
        public static const GET_CONTACTS_EVENT:String = "GET_CONTACTS_EVENT";

        public function GetContactsEvent()
        {
            super( GET_CONTACTS_EVENT );
        }

        override public function clone():Event
        {
            return new GetContactsEvent();
        }
    }
}

Discussion: When creating events keep in mind that events and commands have a one to one ( 1 – 1 ) relationship. When you create an event, it is recommend to create a command that carrys out the process of when that event happens.

For instance you create a GetContactsEvent.as class be sure to create a GetContactsCommand.as class as well, when this event is dispatched the corresponding command will begin to process and carry out the functionality.

 

Recipe 4.5 – What’s a Command?

Problem: You don’t know what a command does

Solution: The Command class then processes the event by running the Command class execute() method, which is an ICommand interface method. The event object may include additional data if required by the developer.

The execute() method can update the central Model, as well as invoke a Service class which typically involves communication with a remote server. The IResponder interface, which is also implemented by the Command class, includes onResult and onFault methods to handle responses returned from the invoked remote service.

GetContactsCommand.as:

package com.jonniespratley.flexcontacts.commands
{
    import com.adobe.cairngorm.commands.ICommand;
    import com.adobe.cairngorm.control.CairngormEvent;
    import com.jonniespratley.flexcontacts.business.FlexContactsDelegate;
    import com.jonniespratley.flexcontacts.events.ContactErrorEvent;
    import com.jonniespratley.flexcontacts.events.GetContactsEvent;
    import com.jonniespratley.flexcontacts.model.FlexContactsModelLocator;
    import com.jonniespratley.flexcontacts.vo.*;

    import mx.collections.ArrayCollection;
    import mx.rpc.IResponder;
    import mx.rpc.events.ResultEvent;
    import mx.utils.ArrayUtil;

    public class GetContactsCommand implements ICommand, IResponder
    {
        private var model:FlexContactsModelLocator = FlexContactsModelLocator.getInstance();

        public function execute( event:CairngormEvent ) : void
        {
            var evt:GetContactsEvent = event as GetContactsEvent;
            var delegate:FlexContactsDelegate = new FlexContactsDelegate( this );
                delegate.getContacts();
        }

      public function result( data:Object ) : void
		{
			trace( 'GetContactsCommand Result Handling' );

			var result:ResultEvent = data as ResultEvent;
			var contactArray:Array = data.result as Array
			var tempAC:ArrayCollection = new ArrayCollection();

			for ( var o:Object in contactArray )
			{
				tempAC.addItem( new ContactVO( contactArray[ o ] ) );
			}
			model.contactsCollection = tempAC;
		}

        public function fault( fault:Object ):void
        {
            var evt:ContactErrorEvent = new ContactErrorEvent( "The was an error",
                            "com.jonniespratley.flexcontacts.commands.GetContacts" );
        }
    }
}

Discussion: Commands come in two flavors, one flavor is if your command implements IResponder then you must be calling a service inside of the delegate of some sort, then if you implement IResponder you must import the mx.rpc.IResponder, since the Cairngorm IResponder has been depreciated.

You include two more functions inside of your command, result() and fault() these functions will handle the results from the server wither it be a success result or a fail result, you can also event dispatch another event from your command, such as when you get a fault from the call you can dispatch a ApplicationFaultEvent() passing in your parameters that you create.

Back to top

Recipe 4.6 – What’s a Delegate?

Problem: You don’t know what a delegate is

Solution: The Delegate in its simplest form is holder of the services in which you want to invoke on the server side. Delegates use the ServiceLocator for locating the service then calling methods on that service.

The Delegate class has generally one argument when being instantiated, which is type IResponder, this provides any class using this Delegate to handle the results of the service in a nice manner.

FlexContactsDelegate.as:

	package com.jonniespratley.flexcontacts.business
{
    import com.adobe.cairngorm.business.ServiceLocator;
    import com.jonniespratley.flexcontacts.vo.ContactVO;

    import mx.rpc.AsyncToken;
    import mx.rpc.IResponder;

    public class FlexContactsDelegate
    {
        private var responder:IResponder;
        private var service:Object;

        public function FlexContactsDelegate( responder:IResponder )
        {
            this.service = ServiceLocator.getInstance().getRemoteObject( "RemotingService" );
            this.responder = responder;
        }

        public function getContacts():void
        {
            var token:AsyncToken = service.getContacts();
                token.addResponder( responder );
        }

        public function removeContact( aId:int ):void
        {
            var token:AsyncToken = service.removeContact( aId );
                token.addResponder( responder );
                token.id = aId;
        }

        public function saveContact( aContact:ContactVO ):void
        {
            var token:AsyncToken = service.saveContact( aContact );
                token.addResponder( responder );
                token.contact = aContact;
        }
    }
}

Discussion: To use a Delegate you will generally create a variable inside of your Command that is of type FlexContactsDelegate, after the type you want to set it equal (= ) to a new instance of that Delegate, and also providing the correct parameter for the argument. We will discuss using this Delegate in a few.

Back to top

Recipe 4.7 – What’s a Service Locator?

Problem: I am not really familiar with the service locator, I know what it looks like but I am not sure what it does

Solution: Well lets create one and talk about it after.

ServiceLocator.mxml:

(<)cairngorm:ServiceLocator xmlns:mx="http://www.adobe.com/2006/mxml"
                          xmlns:cairngorm="com.adobe.cairngorm.business.*" >

    (<)mx:RemoteObject
        id="RemotingService"
        destination="amf"
        endpoint="http://path/to/service"
        source="ContactService"
        showBusyCursor="true"/>

(<)/cairngorm:ServiceLocator>

Discussion: The Cairngorm Service Locator provides a clean and effective way of defining multiple server side sources in a single component; you can define any type of (RPC) remote procedure calls such as HTTP Services, Web Services (WSDL), Remoting Services (AMF), etc. The Service Locator is not created in the form of a class, but as a component based off of the Cairngorm Service Locator package.

Creating this is just as simple as creating a new component, but instead of setting the based on to a VBox or a Canvas you set it to ServiceLocator, finishing it off by declaring the xml namespace to “com.adobe.cairngorm.business.*”.

Back to top

Recipe 4.8 – What’s a Controller?

Problem: You don’t know what a controller is, or does, well you have heard of it, but its not that clear what it actually does.

Solution: The Controller is the most sophisticated part of the Cairngorm architecture. The Controller layer is implemented as a singleton FrontController. The FrontController instance, which receives every View-generated event, dispatches the events to the assigned Command class based on the event’s declared type.
The Front Controller is a central place where all the commands are mapped to the relevant events. Once an event is dispatched, the Front Controller finds the corresponding command to execute.

FlexContactsController.as:

package com.jonniespratley.flexcontacts.control
{
    import com.adobe.cairngorm.control.FrontController;
    import com.jonniespratley.flexcontacts.commands.*;
    import com.jonniespratley.flexcontacts.events.*;

    public final class FlexContactsController extends FrontController
    {
        public function FlexContactsController()
        {
            initilize();
        }

        private function initilize():void
        {
            this.addCommand( GetContactsEvent.GET_CONTACTS_EVENT, GetContactsCommand );
            this.addCommand( LoginEvent.LOGIN_EVENT, LoginCommand );
        }
    }
}

Discussion:

Back to top

Recipe 4.9 – What’s a View?

Problem: You dont’t know what a view is, and how the heck is the view suppose to know what’s going on with the application.

Solution: The View is one or more Flex components such as a button, panel, data grid, combo box, etc. all bundled together as one component. This is what the user will see when using the application, data that is displayed is bound to the Model Locator or other Bindable variables that essential get bound to the model, views also generate events, Cairngorm Events.

ContactsList.mxml

Comming Soon

Discussion:These events can come from any interaction that the developer chooses such as button clicks, component initization, drag-n-drop, mouse clicks, etc. These events get dispatched via the dispatch() method of the Flex framework, once this event is dispatched the controller catches that event and then maps it to the correct command, executing that action.

Back to top

Recipe 4.10 – How does it all work?

Problem:

Solution:

Comming Soon

Discussion:

Back to top