Saturday, April 10, 2010

Flex 3: PureMVC tutorial


PureMVC is free framework which you can use to develop the MVC structured project. As name suggest pureMVC is a framework which is based on the Model, View and Controller. Where you can isolate your business logic with your view elements.

Today I got an opportunity to write a tutorial on Flex development using pureMVC framework. In this tutorial I will walk through the development process using pureMVC framework. The project which I will explain here will have simple data population on datagrid from the XML using HTTPService.

Before we get going, you’ll need the following

  • Flex SDK 3. Its free!
  • Flex Builder 3
  • PureMVC
  • framework library.

    Before I start this tutorial, I assume that you have knowledge about AS3 and Flex. Also you have knowledge of using Flex Builder 3.

    Now we are ready with all required tools. we can start with actual project development. First we will setup the project in Flex Build. Then we will set the packages and folders.

    1. Create flex project in Flex Builder 3 and name it SimplePureMVCExample
    2. add the pureMVC framework library in the lib folder ( add PureMVC_AS3_2_0_4.swc in lib folder of the project). (Refer below image).
    3. Within src folder of the project, create following packages for Model, View and Controller. (Refer below image.)

      com.sm.pureMVC.model.
      com.sm.pureMVC.view.
      com.sm.pureMVC.controller.


    folderStructure

    Now we have all folder ready. I will explain what each folder will have.

    Controller – will have all command classes. Which will use to communicate between Model(Proxy) and view(Mediator)

    Model – will have all business logic classes. Which will process business logic and send the notification.

    View – will have all mediator classes which will listen to all visual elements.

    Application developed using pureMVC framework needs to have an entry point which will startup the pureMVC framework. For this we need to create an ApplicationFacade class. Which will be a heart of the application.

    First we will create an application mxml file which will send a startup notification. which will start the pureMVC.

    Following is the code of main application class.









    If you noticed I have called the startup method on creationComplete event. In this method i have passed the reference of application. The startup method is been called by accessing single tone class ApplicationFacade.


    private function init():void
    {
    ApplicationFacade.getInstance().startup(this);
    }
    This Application mxml has the reference of two visual elements. i.e. Button and Custom component Employee Detail. Which is just an data grid, which will hold the details about the employee. This we will used later with Mediators.



    Now we will move to the next class. i.e. ApplicationFacade. This is a single tone class. This class will extend the Facade class of pureMVC frame work and implement IFacade. This is mandatory that Application Facade class should extend the Facade class of framework and implement the IFacade interface.

    Following is the code sample of Application Facade class.


    package com.sm.pureMVC {
    import com.sm.pureMVC.controller.LoadCommand;
    import com.sm.pureMVC.controller.StartupCommand;

    import org.puremvc.as3.interfaces.IFacade;
    import org.puremvc.as3.patterns.facade.Facade;
    import org.puremvc.as3.patterns.observer.Notification;

    /**
    * @author SM
    */
    public class ApplicationFacade extends Facade implements IFacade {

    public static const NAME : String = 'ApplicationFacade';
    public static const STARTUP : String = NAME + 'Startup';
    public static const LOAD : String = NAME + 'Load';
    public static const DATA_LOADED : String = NAME + 'dataLoaded';

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

    override protected function initializeController() : void {
    super.initializeController();
    registerCommand(STARTUP, StartupCommand);
    registerCommand(LOAD, LoadCommand);
    }

    public function startup(application : PureMVCExample) : void
    {
    sendNotification(STARTUP, application);
    }

    override public function sendNotification(notificationName : String, body : Object = null, type : String = null) : void {
    notifyObservers(new Notification(notificationName, body, type));
    }
    }
    }

    In this class you have noticed that, there are few const declared in the beginning. These are the names of the commands. Which will be access during sending notification.


    public static const NAME : String = 'ApplicationFacade';
    public static const STARTUP : String = NAME + 'Startup';
    public static const LOAD : String = NAME + 'Load';
    public static const DATA_LOADED : String = NAME + 'dataLoaded';
    The initialize Controller method will used to register all controllers(Commands).This is an overridden method of Facade class. and this will call the super method and then it will register all controllers. To register the command we will use the registerCommand method and we will pass two parameters. First is Name of the command and second is the command class.

    override protected function initializeController() : void {
    super.initializeController();
    registerCommand(STARTUP, StartupCommand);
    registerCommand(LOAD, LoadCommand);
    }
    The startup public method will be called from the main application mxml file. and this will send a notification of STARTUP command. and it will also send the reference to the main application. This notification will execute the Startup command.

    public function startup(application : PureMVCExample) : void
    {
    sendNotification(STARTUP, application);
    }

    Now we have send the STARTUP notification. We will need to create an StartupCommand class within com.sm.pureMVC.controller package. This class will extend the SImpleCommand of pureMVC framework and implements the ICommand. This is the entry command which will initiate the Proxy and Mediator. Whenever the startup notification is send it will trigger the execute method of Startup Command

    Following is the code sample of StartupCommand class.


    package com.sm.pureMVC.controller {

    import com.sm.pureMVC.model.EmployeeServiceProxy;
    import com.sm.pureMVC.view.ApplicationMediator;

    import org.puremvc.as3.interfaces.INotification;
    import org.puremvc.as3.patterns.command.SimpleCommand;

    /**
    * @author SM
    */
    public class StartupCommand extends SimpleCommand {

    /**
    * Initialize the model and views
    */
    override public function execute(notification:INotification):void
    {
    facade.registerProxy(new EmployeeServiceProxy());
    facade.registerMediator(new ApplicationMediator(notification.getBody() as PureMVCExample));
    }
    }
    }

    The execute overridden method will have the code for initializing the EmployeeServiceProxy proxy and ApplicationMediator mediator. The application reference passed along with startUp Notification can be accessed by notification parameter of execute method. i.e by notification.getBody(); and this will be passed to the Mediator as a parameter.


    override public function execute(notification:INotification):void
    {
    facade.registerProxy(new EmployeeServiceProxy());
    facade.registerMediator(new ApplicationMediator(notification.getBody() as PureMVCExample));
    }
    Now we are ready with startup command class. Lets move to create EmployeeServiceProxy class. We will create this proxy class in com.sm.pureMVC.model package.This class will hold the logic of loading XML using http Service. In this class we will create the service which will load the employee.xml on call of onLoad method from outside. On http Service result we will send the notification of DATA_LOADED along with an array collection. This will be listen by EmployeeDetailMediator class employee datagrid component.

    Following is the code sample of EmployeeServiceProxy class.


    package com.sm.pureMVC.model
    {


    import com.sm.pureMVC.ApplicationFacade;

    import mx.collections.ArrayCollection;
    import mx.controls.Alert;
    import mx.rpc.events.FaultEvent;
    import mx.rpc.events.ResultEvent;
    import mx.rpc.http.mxml.HTTPService;

    import org.puremvc.as3.interfaces.IProxy;
    import org.puremvc.as3.patterns.proxy.Proxy;

    public class EmployeeServiceProxy extends Proxy implements IProxy
    {
    public static const NAME:String = 'EmployeeProxy';

    private var serv:HTTPService;

    public function EmployeeServiceProxy(data:Object = null)
    {
    super(NAME, data);
    setService()
    }

    private function setService():void
    {
    serv = new HTTPService();
    serv.url="assets/data/employee.xml"
    serv.resultFormat="object"
    serv.addEventListener(ResultEvent.RESULT,servResultHandler);
    serv.addEventListener(FaultEvent.FAULT,servFaultHandler);
    }

    private function servResultHandler(event:ResultEvent):void
    {
    sendNotification(ApplicationFacade.DATA_LOADED, event.result.Employee_Info.Employee as ArrayCollection);
    }

    private function servFaultHandler(event:FaultEvent):void
    {
    Alert.show(event.toString());
    }

    public function loadData():void
    {
    serv.send();
    }

    override public function getProxyName():String
    {
    return EmployeeServiceProxy.NAME;
    }
    }
    }

    Declare the proxy name const in the beginning. This cont can be used, when we have to access the proxy class using retriveProxy method of Facade class.

    public static const NAME:String = 'EmployeeProxy';
    The setService method will be called from the constructor and it will initialize the http service and set the required properties and listeners.

    private function setService():void
    {
    serv = new HTTPService();
    serv.url="assets/data/employee.xml"
    serv.resultFormat="object"
    serv.addEventListener(ResultEvent.RESULT,servResultHandler);
    serv.addEventListener(FaultEvent.FAULT,servFaultHandler);
    }
    The loadData method will be called from the outside. This method will send the http service request. The result/fault of the request will be handle in the respective handler methods.


    public function loadData():void
    {
    serv.send();
    }
    The servResultHandler will triggered on result of service. This function will send the notification for data loaded along with the data object. This notification will be listen by the interested mediators(in this project EmployeeDetailMediator).


    private function servResultHandler(event:ResultEvent):void
    {
    sendNotification(ApplicationFacade.DATA_LOADED, event.result.Employee_Info.Employee as ArrayCollection);
    }
    Now we have set the proxy class. we will move to create the application mediator class in the com.sm.pureMVC.view package.

    This class will extend the Proxy Class and implement the IProxy interface. This class will register the EmployeeDetailMediator and set the listener of button to load the xml.

    Following is the code sample of EmployeeServiceProxy class.

    package com.sm.pureMVC.view
    {
    import com.sm.pureMVC.ApplicationFacade;

    import flash.events.MouseEvent;

    import org.puremvc.as3.interfaces.IMediator;
    import org.puremvc.as3.interfaces.INotification;
    import org.puremvc.as3.patterns.mediator.Mediator;

    public class ApplicationMediator extends Mediator implements IMediator
    {
    public static const NAME:String = 'ApplicationMediator';

    public function ApplicationMediator(viewComponent:Object=null)
    {
    super(ApplicationMediator.NAME, viewComponent);
    facade.registerMediator(new EmployeeDetailMediator(application.employeeList));
    application.getEmployee.addEventListener(MouseEvent.CLICK, getEmployeeClickHandler);
    }

    override public function getMediatorName():String
    {
    return ApplicationMediator.NAME;;
    }

    private function get application():PureMVCExample
    {
    return viewComponent as PureMVCExample;
    }

    private function getEmployeeClickHandler(event:MouseEvent):void
    {
    sendNotification(ApplicationFacade.LOAD);
    }

    }
    }

    Declare the Mediator name const in the beginning. This cont can be used, when we have to access the mediator class using retriveMediator method of Facade class.


    public static const NAME:String = 'ApplicationMediator';
    The application reference passed with ApplicationMediator will be stored in the viewComponent property of the class. In the constructor we will register another mediator i.e. EmployeeDetailMediator and we will pass the reference of the employee detail component to this mediator as a parameter. This will map the employee detail component with EmployeeDetailMediator.

    Also we will add the listener to the getEmployee button which is present in the application mxml.

    public function ApplicationMediator(viewComponent:Object=null)
    {
    super(ApplicationMediator.NAME, viewComponent);
    facade.registerMediator(new EmployeeDetailMediator(application.employeeList));
    application.getEmployee.addEventListener(MouseEvent.CLICK, getEmployeeClickHandler);
    }
    On getEmployee button click handler we will send the notification of Load command. This will be listen by the LoadCommand.

    private function getEmployeeClickHandler(event:MouseEvent):void
    {
    sendNotification(ApplicationFacade.LOAD);
    }
    Now we will create the LoadCommand class in com.sm.pureMVC.controller package. Like Startup command this command class will also extend the SimpleCommand and implements the ICommand. This class will execute when Load notification is sent. In this application the load notification is been sent from the getEmployee click handler.

    Following is the code sample of EmployeeServiceProxy class.

    package com.sm.pureMVC.controller
    {
    import com.sm.pureMVC.model.EmployeeServiceProxy;

    import org.puremvc.as3.interfaces.ICommand;
    import org.puremvc.as3.interfaces.INotification;
    import org.puremvc.as3.patterns.command.SimpleCommand;

    public class LoadCommand extends SimpleCommand implements ICommand
    {
    public function LoadCommand()
    {
    super();
    }

    override public function execute(notification:INotification):void
    {
    var employeeProxy:EmployeeServiceProxy = facade.retrieveProxy(EmployeeServiceProxy.NAME) as EmployeeServiceProxy;
    employeeProxy.loadData();
    }

    }
    }
    The execute overridden method will have the code for calling loadData method of EmployeeServiceProxy class. If you have noticed here we hate retrieve the reference of EmployeeServiceProxy class using retrieveProxy method of facade class. To retrieve the reference we have passed the EmployeeServiceProxy name const. This will intimate facade that the request is for EmployeeServiceProxy class.

    override public function execute(notification:INotification):void
    {
    var employeeProxy:EmployeeServiceProxy = facade.retrieveProxy(EmployeeServiceProxy.NAME) as EmployeeServiceProxy;
    employeeProxy.loadData();
    }
    Now we are ready with Load command class, next we will create the EmployeeDetailMediator class. This class will extend the Mediator and implement the IMediator interface. Remember we have register this mediator in the ApplicationMediator class constructor and we have passed the reference of employee detail component to this.

    In this class we have to listen to DATA_LOADED notification, which was sent from the EmployeeServiceProxy class result handler method. and set the employee data to the data grid which was passed along with DATA_LOADED notification.

    Following is the code sample of EmployeeServiceProxy class.

    package com.sm.pureMVC.view
    {
    import com.sm.pureMVC.ApplicationFacade;
    import com.sm.pureMVC.view.components.EmployeeDetail;

    import mx.collections.ArrayCollection;

    import org.puremvc.as3.interfaces.IMediator;
    import org.puremvc.as3.interfaces.INotification;
    import org.puremvc.as3.patterns.mediator.Mediator;

    public class EmployeeDetailMediator extends Mediator implements IMediator
    {
    public static const NAME:String = 'EmployeeDetailMediator';

    public function EmployeeDetailMediator(viewComponent:Object=null)
    {
    super(EmployeeDetailMediator.NAME, viewComponent);
    }

    override public function getMediatorName():String
    {
    return EmployeeDetailMediator.NAME;
    }

    private function get employeeDetail():EmployeeDetail
    {
    return viewComponent as EmployeeDetail;
    }

    override public function listNotificationInterests():Array
    {
    return [ ApplicationFacade.DATA_LOADED];
    }

    override public function handleNotification(notification:INotification):void
    {
    switch (notification.getName()) {
    case ApplicationFacade.DATA_LOADED:
    employeeDetail.employee.dataProvider = notification.getBody() as ArrayCollection;
    break;
    }
    }

    }
    }

    To listen to the notification in mediator we have to follow few steps. First we have to add the notification which are interested by this mediator using listNotificationInterests method. In this overridden method we will return the array of notification names which will be interested by this mediator class.


    override public function listNotificationInterests():Array
    {
    return [ ApplicationFacade.DATA_LOADED];
    }
    Now, after adding the interested notification. we will create the function which will execute respective functionality for interested notification. The handleNotification overridden method will have the code for executing specific functionality for each notification. If the mediator is interested in multiple notification. that can be listen using switch case loop.

    In this function, within DATA_LOADED case we will set the employee data grid data provider. Remember we have sent the data object reference along with the DATA_LOADED notification. We will access this data object by getBody method of notification.

    override public function handleNotification(notification:INotification):void
    {
    switch (notification.getName()) {
    case ApplicationFacade.DATA_LOADED:
    employeeDetail.employee.dataProvider = notification.getBody() as ArrayCollection;
    break;
    }
    }

    That’s it. we have complete the simple flex application using pureMVC framework. I hope this tutorial will help you to understand the flow of developing the flex application using pureMVC. If you have any question, Kindly let me know I will try to answer you question.

    Summary of development.

    1. Call startup method of ApplicationFacade class from the application mxml file.

    2. Send the startup notification from the ApplicationFacade class.

    3. Initialize the ApplicationMediator and EmployeeServiceProxy in the Startup command.

    4. Add the get employee button listener in the ApplicationMediator class.

    5. Send notification of LOAD command from the ApplicationMediator class

    6. Initialize the EmployeeDetailMediator mediator in the ApplicationMediator class.

    7. Add logic of xml loading using httpService in the EmployeeServiceProxy class.

    8. Send notification of DATA_LOADED from the EmployeeServiceProxy class.

    9. Handle DATA_LOADED notification in EmployeeDetailMediator class.


    Download the complete source zip file

    5 comments:

    1. Hey Shailesh, Nice Explanation. Thanks for posting.

      ReplyDelete
    2. good job man. Thanks for your wonderful posting.

      ReplyDelete
    3. hey that was really nice one...am a new bie...i have a issue that am getting "Security Sandbox Violation"

      ReplyDelete
    4. @bobbysharmaSend me exact detail about the issue you are facing. So that I can reply to you.

      Thank you

      ReplyDelete