ActionScript Foundry: a beginner’s guide

Each of the three ActionScript libraries (commons, foundry, toolbox) has its own responsibilities. The foundry library is a MVC framework which provides design solutions needed to build applications and deal with their complexity. It is an aggregation of design patterns.

If you are not familiar with MVC concerns, we recommend you to read an introduction to the MVC design pattern and its implementation in the Foundry : “The MVC Framework“.

First steps

In a MVC-compliant application, responsibilities are shared between :

  • one or several models which store the data and use a notification mechanism to keep the views up-to-date,
  • one controller and possibly several sub-controllers that perform application-wide operations,
  • several views which display the data to the users and handle users operations.

First, we prepare the package structure that will contain our classes. You may create your own package structure, but to have one which is consistant with the conceptual model is a good idea. Our application will be composed of three packages, one for each of the participants :

  • com.company.sample.control
  • com.company.sample.model
  • com.company.sample.view

We will now create the most important participants of our application : the Model and the Controller.

Creating the Model

The main purpose of Models is to maintain the state of the application and to expose it to consumers. One of the main challenges in Flex development is to keep the views synchronized with the data, especially when several views need to share subsets of data. The foundry provides a notification mechanism based upon the Observer pattern.

To create our Model, we will simply extend the org.servebox.foundry.model.AbstractModel class and add the accessors needed to store and retrieve data. Please note that we use the notifyObservers method so our SampleModel class will be able to keep the application up-to-date.

// com/company/sample/model/SampleModel.as
 
package com.company.sample.model
{
	import mx.collections.ArrayCollection;
 
	import org.servebox.foundry.model.AbstractModel;
	import org.servebox.foundry.observation.SimpleNotification;
        /**
         * Sample implementation of the Model participant.
         */
	public class SampleModel extends AbstractModel
	{
 
	    //-----------------------------------------------------------------
            // Accessors to store and retrieve data
	    //-----------------------------------------------------------------
 
	    private var _customers : ArrayCollection;
            /**
             * The customers collection to store and retrieve into the Model.
             */
            public function getCustomers() : ArrayCollection
	    {
		return _customers
	    }
 
	    public function setCustomers( ar : ArrayCollection ) : void
	    {
		_customers = ar;
                // Each time the value of customers will be set, a SimpleNotification
                // will be broadcasted to the registered observers.
	        notifyObservers( new SimpleNotification( _customers ) );
	    }
 
	}
}

Creating the Controller

The controller translates users interactions coming from the view into operations to be handled by the business delegates (see “Accessing remote services” to know more about BusinessDelegate) or one of the application models.

To create our application’s controller, we simply extend the AbstractController class. As it is the main entry point for all of the application-wide operations, the Controller has a few requirements :

  • it should implement the Singleton design pattern, so that we could always obtain the same controller instance.
  • it has the responsibility to instantiate models, business delegates and sub-controllers. To do so, we will override initializeModels, initializeDelegates, and initializeSubControllers methods.

In the following sample code, we use the initialization methods to create our SampleModel instance and register it at the ModelLocator.

// com/company/sample/control/SampleController.as
 
package com.company.sample.control
{
    import com.company.sample.model.SampleModel;
 
    import org.servebox.foundry.control.AbstractController;
    import org.servebox.foundry.model.ModelLocator;
 
    /**
     * Sample implementation of the Controller participant.
     */
    public class SampleController extends AbstractController
    {
        //-----------------------------------------------------------------
        // Singleton access
        //-----------------------------------------------------------------
 
        private static var instance : SampleController;
 
        public static function getInstance() : SampleController
        {
            if ( instance == null )
            {
                instance = new SampleController();
            }
            return instance;
        }		
 
        //-----------------------------------------------------------------
        // Initialization methods
        //-----------------------------------------------------------------
        override public function initializeModels():void
        {
            // We create our model instance and register it to
            // ModelLocator so it can be referenced from anywhere
            // in the application.
            ModelLocator.getInstance().registerModel( "SampleModel" , new SampleModel() );
        }
 
        override public function initializeDelegates():void
        {
            // Do-nothing, we will cover the access to
            // the remote services in another article
        }
 
    }
}

Now that we have created both the Model and Controller participants we are ready to wrap them into an application and to start creating views.

Creating the application

The top-level element of a Flex Application istypically an mx.core.Application instance (mx:Application MXML tag). An MVC framework such as the Foundry requires a certain number of initialization tasks to be performed before the application is ready to run. To automate these tasks, the foundry provides the AbstractApplication class.

We create the application class by using AbstractApplication as the base Class, and override the getControllerClass method to tell the application which type to use as the main controller.

// com/company/sample/control/SampleApplication.as
 
package com.company.sample.control
{
    import org.servebox.foundry.control.AbstractApplication;
 
    /**
     * Our sample application base class.
     */
    public class SampleApplication extends AbstractApplication
    {
        override protected function getControllerClass() : Class
        {
	        return SampleController;
        }
    }
}

Our application is ready to go, we just tell the Flex compiler to use it as the application class by creating the main MXML file.

<?xml version="1.0" encoding="utf-8"?>
<!-- Main.mxml -->
<control:SampleApplication
    xmlns:mx="http://www.adobe.com/2006/mxml"
    xmlns:control="com.company.sample.control.*">
 
&lt;/control:SampleApplication&gt;

Designing a view

In Flex applications, a View can virtually be any Container provided by the Flex library. To add formatting and view-logic code, a developer typically extends one of these container classes. The resulting code mixes  up the code related to presentation formatting with the code related to the users interaction support or the local data processing. The foundry provides a mechanism – based on a design pattern called ViewHelper – which enables to share the responsibilities between two participants. The View contains only formatting code and delegates its management logic to the ViewHelper.

Creating the View

To create a view, we add a MXML file using one of the available types (CanvasView, HBoxView, VBoxView…) as the top-level tag, here HBoxView.

<?xml version="1.0" encoding="utf-8"?>
<!-- com/company/sample/view/CustomersView.mxml -->
<view:HBoxView
    xmlns:view="org.servebox.foundry.view.*"
    xmlns:mx="http://www.adobe.com/2006/mxml"
    width="100%"
    height="100%">
 
</view:HBoxView>

Creating the ViewHelper

Using MXML files to describe your application views will automatically mix-up the code related to the presentation with the view logic functions and properties. Even if it may sometimes appear easier, in most of cases the resulting source code will be confusing and hard to maintain. The Foundry provides a separation between the presentation and the logic. Each view is associated with a ViewHelper, containing the logic – calls to controller, events and data model notification handling, data bindings. This way, the view can be easily designed using MXML tags. You could for example edit with a WYSIWYG IDE such as Adobe Flex Builder. The logic code is easier to maintain because it is located in a separate class, and can be reused or extended.

We create the CustomersViewHelper class which contains a bindable accessor to the “customers” collection. As an implementor of the IObserver interface, our ViewHelper can register at the SampleModel and listen to notifications by overriding the update method.

// com/company/sample/view/CustomersViewHelper.as
 
package com.company.sample.view
{
    import com.company.sample.model.SampleModel;
 
    import flash.events.Event;
 
    import mx.collections.ArrayCollection;
 
    import org.servebox.foundry.model.ModelLocator;
    import org.servebox.foundry.observation.IObservable;
    import org.servebox.foundry.observation.Notification;
    import org.servebox.foundry.view.ViewHelper;
 
    /**
     * Sample implementation of the ViewHelper design pattern.
     */
    public class CustomersViewHelper extends ViewHelper
    {
        /**
         * Accessor to the customers collection.
         */
        public function get customers() : ArrayCollection
        {
            return SampleModel(
                       ModelLocator.getInstance().getModel("SampleModel")
                   ).getCustomers();
        }
 
        /**
         * Registers the ViewHelper instance to the SampleModel.
         */
        override public function registerToModels():void
 
        {
            // We register as an observer of the SampleModel.
            // The string litterlal "SampleModel" should be replaced
            // with more elegant code, but we won't obfuscate the
            // code sample.
 
            SampleModel(
                ModelLocator.getInstance().getModel("SampleModel")
            ).registerObserver( this );
 
        }
 
        /**
         * Will be triggered each time the Model broadcasts a notification.
         */
        override public function update( o:IObservable, n:Notification ):void
 
        {
            // Dispatch the event so the binding is triggered
            dispatchEvent( new Event("customersChange") );
        }
    }
}

Adding the view to our application

Before adding the view to the application, we need to associate it with its ViewHelper. This can be done by adding a simple tag into the MXML code.

<?xml version="1.0" encoding="utf-8"?>
<!-- com/company/sample/view/CustomersView.mxml -->
<view:HBoxView
    xmlns:view="org.servebox.foundry.view.*"
    xmlns:mx="http://www.adobe.com/2006/mxml"
    width="100%"
    height="100%">
 
    <view:CustomersViewHelper id="helper"/>
 
</view:HBoxView>

Once this simple step achieved, we can add the view to the application, for example by placing it into a TabNavigator.

<?xml version="1.0" encoding="utf-8"?>
<!-- Main.mxml -->
<control:SampleApplication
    xmlns:mx="http://www.adobe.com/2006/mxml"
    xmlns:control="com.company.sample.control.*"
    xmlns:view="com.company.sample.view.*">
 
    <mx:TabNavigator width="80%" height="80%" horizontalCenter="0" verticalCenter="0">
 
        <view:CustomersView
            id="customersView"
            width="100%"
            height="100%"
            label="Customers Management"/>
 
    </mx:TabNavigator>
 
</control:SampleApplication>

Next steps

We have created the three participants of our MVC-enabled application. Next steps is to access remote data services to fetch our customers collection.

Previous Step

Next Step

Monday, November 17th, 2008 No Comments by Jeff Mathiot