Cairngorm Getting Started Part Four : Events, Commands and Controllers

So we’ve had a chat about the MVC design pattern and how that relates to Cairngorm, set up the environment and we’ve discussed how Singletons work.  Now we are ready to start using the framework to create a basic application.

Frameworks like Cairngorm really come into their own when you are creating a large or relatively complicated application.  For the purposes of this little demonstration we will load an XML file and display it in either a Tree component or in a TextArea component.  No great shakes and I know that if this was all you really needed to to then using Cairngorm is a little bit of an overkill, but keeping it simple means we’ll clearly be able to look at what the framework does without having to go over lots of code.

To just go back a step for a moment we are going to quickly recap how the Cairngorm framework operates.

The view dispatches events to the controller.  The controller then either modifies the model directly or in the case of getting some data, creates a delegate and a responder.  The delegate gets the data and returns it to the responder that modifies the model.  Then the model tells the view what to display.

Ideally each set of classes should only operate in an expected way on the other classes.  IE the models should only ever be modified by Controllers or Responders, the Delegates should be the only classes that get data, the views should only do view stuff.  This is the delegation of responsibility.

Lots of books and tutorials almost talk about classes as if they are sentient beings, the view shouldn’t know about the model, the model shouldn’t know about the controller.  This sort of worked for me, but I can’t really anthropomorphise what I know is just a set of classes.

Another way of looking at this, and one which makes sense to me, is that it’s all about organisation.  You wouldn’t file your car tax in the fridge, otherwise you’ll spend ages looking for it when you need to renew it.  Likewise with a Cairngorm app if something is wrong with the data call you know to start at the Delegate and see what is happening there.  (If you wanted a new car you wouldn’t want to have to change the fride too.  This analogy doesn’t quite hold up, but you know what I mean).

So if we’re happy with that lets get on with the code.

We’ll start first with the base mxml application file.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application layout="vertical">

<mx:Script>
<![CDATA[
]]>
</mx:Script>

<mx:Panel title="XML Display" width="600" height="500">
<mx:ViewStack width="100%" height="100%">
<mx:Box>
<mx:Tree/>
</mx:Box>
<mx:Box>
<mx:TextArea/>
</mx:Box>
</mx:ViewStack>
<mx:ControlBar>
<mx:Button id="btn_tree" label="Display as Tree"/>
<mx:Button id="btn_datagrid" label="Display as TextArea"/>
</mx:ControlBar>
</mx:Panel>

</mx:Application>

Nice and simple.  So first things first we need to get the XML.  I’ve created a very very simple XML file.

<?xml version="1.0" encoding="utf-8"?>
<exampleXML>
<block title="Block One">
<item name="Item One"/>
<item name="Item Two"/>
<item name="Item Three"/>
<item name="Item Four"/>
</block>
<block title="Block Two">
<item name="Item One"/>
<item name="Item Two"/>
<item name="Item Three"/>
<item name="Item Four"/>
<item name="Item Five"/>
<item name="Item Six"/>
<item name="Item Seven"/>
</block>
<block title="Block Three">
<item name="Item One"/>
<item name="Item Two"/>
<item name="Item Three"/>
<item name="Item Four"/>
<item name="Item Five"/>
<item name="Item Six"/>
</block>
<block title="Block Four">
<item name="Item One"/>
<item name="Item Two"/>
<item name="Item Three"/>
<item name="Item Four"/>
<item name="Item Five"/>
<item name="Item Six"/>
<item name="Item Seven"/>
<item name="Item Eight"/>
<item name="Item Nine"/>
<item name="Item Ten"/>
<item name="Item Eleven"/>
<item name="Item Twelve"/>
</block>
<block title="Block Five">
<item name="Item One"/>
<item name="Item Two"/>
<item name="Item Three"/>
<item name="Item Four"/>
</block>
<block title="Block Six">
<item name="Item One"/>
<item name="Item Two"/>
<item name="Item Three"/>
<item name="Item Four"/>
<item name="Item Five"/>
<item name="Item Six"/>
</block>
</exampleXML>

To trigger the load we’ll need to dispatch an event.  This is a very simple event so it’ll be a very simple class.  I’ve called it LoadXMLEvent and it’s saved in the event folder we created in part Two.  Right click on the folder, select new class.  When the class wizard launches we need to extend the CairngormEvent class.  Select the browse button next to the Superclass text input and then start to type ‘CairngormEvent’ into the panel that pops up.  It’ll quickly find CairngormEvent, click OK and then OK again into the wizard panel.  This will generate the class with the correct imports and will also create the constructor for you.

Like I said this is a very simple class, it doesn’t need to even parse any data, all we want to do is announce we need the XML.

package com.worthyashes.simpleCairngorm.events
{
import com.adobe.cairngorm.control.CairngormEvent;

import flash.events.Event;

public class LoadXMLEvent extends CairngormEvent
{
public static const LOAD_XML_EVENT:String = "loadXMLEvent";

public function LoadXMLEvent(bubbles:Boolean=false, cancelable:Boolean=false)
{
super(LOAD_XML_EVENT, bubbles, cancelable);
}

override public function clone():Event
{
return new LoadXMLEvent(this.bubbles,this.cancelable);
}

}
}

Lets go through the code.  The first variable is a public static constant variable (it’s in capitals as a visual indication that it is a constant, this is not necessary though it is a convention worth following.)

All this really does is hold the string to indicate what event this class is.  CairngormEvents exetend the standard Event class so it requires a string indicator.  Rather than parseing the name of the event in when the event is created by the class using it we are it in the class so we have more control, and access to it through the static variable.  This means that we don’t have to try and remember what we called this event, if we need to access this name all we need to do is use LoadXMLEvent.LOAD_XML_EVENT, and we make sure that we are talking about the correct event.

Now for the constructor.  As we are not parseing any data we don’t really have to put anything in the constructor, but as it’s best practice (and the code was written for us) we’ll leave the variables for bubbling and canceleable in.

Note that in the super() function, that calls the Superclasses constructor we are parsing the LOAD_XML_EVENT variable in to type the class.

Finally we override the clone function.  This function is used for bubbling and by overriding this function we make sure the correct event type is returned.

That’s it – our first CairngormEvent, woo hoo!

Next we’ll create the Controller that’ll actually do something.

On the commands folder right click, select new , class and then in the wizard rather than selecting a superclass we are going to select an interface.  The ICommand interface types this class as an ICommand and also defines the functions required by it.

Select the add button next to the Interface text area, start typing in ICommand and when it finds the correct interface select it and hit OK.  Select OK on the wizard panel and your controller will be created with all of the required imports and also the required functions to satisfy the interface.

Now onto the class

package com.worthyashes.simpleCairngorm.commands
{
import com.adobe.cairngorm.commands.ICommand;
import com.adobe.cairngorm.control.CairngormEvent;
import com.worthyashes.simpleCairngorm.events.LoadXMLEvent;

public class LoadXMLCommand implements ICommand
{
public function LoadXMLCommand()
{
}

public function execute(event:CairngormEvent):void
{
var e:LoadXMLEvent = event as LoadXMLEvent;

trace("Load XML Event triggered the command!!");
}

}
}

All we’ve done here is populate the execute function with a trace so we know it’s working.  I also set a variable, in this case e as the Type of event that has triggered this command and cast the event passed into the function to the correct type.  In other classes this can be useful as this then gives you native access to the Event classes properties.  In this case the LoadXMLEvent has no properties, it only exists to tell the application it is time to get some XML, but I still have it there.  It’s not necessary, but after you’ve been putting together a large application you have a pile of classes, having a reference to the event class in the command class makes it easier to find.

So we’ve created an Event and we’ve created a Command – but aren’t we doing an MVC implementation?  Where’s the controller?  That comes now and the controller exists to tie the Events and Commands together.

Right click on the control folder and select new class.  This class extends FrontController and I’ve called mine SimpleFrontController.
The FrontController class listens to the CairngormEventDispatcher class and executes Commands based on the events called.

Lets add our code to start it working.

package com.worthyashes.simpleCairngorm.control
{
import com.adobe.cairngorm.control.FrontController;
import com.worthyashes.simpleCairngorm.commands.*;
import com.worthyashes.simpleCairngorm.events.*;

public class SimpleFrontController extends FrontController
{
public function SimpleFrontController()
{
super();
initialise();
}

private function initialise():void
{
addCommand(LoadXMLEvent.LOAD_XML_EVENT,LoadXMLCommand);
}

}
}

We’ve created our own initialise function and in this function we tie the commands and the events together.  Remeber the static string we created in the LoadXMLEvent class that was then passed to the super function as the type?  This is an example of why we do this – we don’t need to remember the type, all we need to do is reference the Event class itself.  This is really handy if we need to change the type – rather than picking through the app changing strings we change it in the Event class itself and that’s it.

Now we’ve created the controller we need to instantiate it.  This can be done with MXML in the application.  We’ll also trigger the event.

<?xml version="1.0" encoding="utf-8"?>
<mx:Application layout="vertical"
xmlns:control="com.worthyashes.simpleCairngorm.control.*"
creationComplete="creationCompleteHandler();">

<mx:Script>
<![CDATA[
private function creationCompleteHandler():void
{
var loadXMLEvent:LoadXMLEvent = new LoadXMLEvent();
loadXMLEvent.dispatch();
}
]]>
</mx:Script>

<control:SimpleFrontController id="frontController"/>

<mx:Panel title="XML Display" width="600" height="500">
<mx:ViewStack width="100%" height="100%">
<mx:Box>
<mx:Tree/>
</mx:Box>
<mx:Box>
<mx:TextArea/>
</mx:Box>
</mx:ViewStack>
<mx:ControlBar>
<mx:Button id="btn_tree" label="Display as Tree"/>
<mx:Button id="btn_datagrid" label="Display as TextArea"/>
</mx:ControlBar>
</mx:Panel>

</mx:Application>

What have we added?  First of all we’ve added an xmlns attibute to the main application tag.  This allows us to shortcut accessing the controller by using our own name ‘control’ that will reference the com.worthyashes.simpleCairngorm.control package.  We’ve also added a creation complete handler function for the creationComplete event.  This function creates and dispatches an loadXMLEvent.  Finally we’ve added the FrontController using MXML.  Run it and you will get the trace from the Command class as it executes – don’t forget to debug the application rather than running it.  Next we are going to look at the Delegate that retrieves the data and introduce a new class, the ServiceLocator class.

Tags »

Author:admin
Date: Wednesday, 30. July 2008 15:20
Trackback: Trackback-URL Category: Cairngorm, Flex, Frameworks, Tutorials

Feed for the post RSS 2.0 Comment this post

1 Comment

  1. Recent Links Tagged With "cairngorm" - JabberTags
    Thursday, 18. December 2008 8:34
1

[...] PureMVC Frustrations and Code Behind Thoughts – (1 of 28) Saved by EMarketMan on Wed 03-12-2008 Cairngorm Simple Example Part Four : Events, Commands and Controllers Saved by johncase142 on Wed 03-12-2008 A Journey – Part 2: Unit Testing in Cairngorm Saved by [...]

Submit comment