Simple MVC for Flex and AIR

This weekend I tried to make an “Air” client for search using “Adobe Community Help. I had no prior experience with Flex and AIR. My first attempt ended as a simple project for college, a spaghetti code. All examples found on the web or official documentation are based on massive use mxml code. I ended up having a glorious mxml file. Huge :-).

Some smart guys are telling you to split your mxml using an ActionScript file but is like putting the trash under the carpet. It is still spaghetti code but in two dishes.

I tried to remember how I would have programmed it in Java Swing.  9 years have passed since then but I remembered. TableModel, DefaultListModel, ButtonModel etc. they all came out from a dark corner of my memory. Yes ! I remembered, Swing MVC.

A typical sample from documentation…..

   1: <?xml version="1.0" encoding="utf-8"?>
   2: <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
   3:     <mx:Panel x="10" y="10" width="350" height="200" title="Rate Customer Service">
   4:         <mx:ComboBox id="cbxRating" x="20" y="20" width="100">
   5:             <mx:dataProvider>
   6:                 <mx:Array>
   7:                     <mx:String>Satisfied</mx:String>
   8:                     <mx:String>Neutral</mx:String>
   9:                     <mx:String>Dissatisfied</mx:String>
  10:                 </mx:Array>
  11:             </mx:dataProvider>
  12:         </mx:ComboBox>
  13:         <mx:Button x="140" y="20" label="Send"/>
  14:     </mx:Panel>
  15: </mx:Application>

Who hard-codes his application in this way? In a typical application you get this stuff from files or database. But let’s see the good part, at least is giving you a hint, a ComboBox and List are accepting an Array as input.

MVC applied to Flex

First of all a briefing about MVC

  1. Model, is the data. Manipulates the internal state and fire events when the internal state changed.
  2. View, the visual representation for the Model’s data (controls on the screen)
  3. Controller, is responsible for interpreting the user actions on the view and make changes to the model. (usually an event handler in flex)

In reality there is no 100% demarcation between these three layers. Is not that easy to make them completely decoupled and usually we end up making some tradeoffs.

The controller will always know about the view and the view about the controller. Controller also knows about the model. In the end I could say that the model is the only piece of the MVC that can be “100% decoupled”.

Let’s try to put a simple screen like this into MVC.

image

We fill in some text and then press Add. After we press add the text box is cleared and the text is added into de the list. In the end wee look into it and try to make a reusable component.

Model

The model contains the actual data, and for our example it should a class that hold list values. As I pointed out earlier we could use an ArrayCollection to store the list. For this we make an actionscript class named ListModel

The view should display model’s changes and for this we mark the model class as [Bindable]. This is nice feature of ActionScript, no such thing in Java Swing.

Now, to add a new item in the list we simply nee an addElement function. In this example we make the model a singleton. This means that if we make more than one list they will share the same data.

   1: package org.bserban.flex.simplemvc.model
   2: {
   3:     import mx.collections.ArrayCollection;
   4:     /**
   5:     * Model class used to store list data and to
   6:     * provide access to the list content.
   7:     *
   8:     * For demo purposes, we have only addElement.
   9:      */
  10:     [Bindable]
  11:     public class ListModel
  12:     {
  13:         public var items :ArrayCollection = new ArrayCollection();
  14:         private static var instance:ListModel;
  15:         public function ListModel()
  16:         {
  17:             instance=this;
  18:         }
  19:         /**
  20:          * For this demo we treat this class as singletone.
  21:          * If list is reused the component that reuse it
  22:          * must keep reference to model and controler.
  23:          */
  24:         public static function getInstance():ListModel{
  25:             if(instance == null){
  26:                 instance = new ListModel();
  27:             }
  28:             return instance;
  29:         }
  30:         /**
  31:          * Add a new element to the list, the model is bindable,
  32:          * no further actions are required, the view
  33:          * is notified automatically.
  34:          */
  35:         public function addElement(itm:String): void{
  36:             items.addItem(itm);
  37:             trace(" item added, now we have:"+items.length);
  38:         }
  39:     }
  40: }

Controller

The controller is responsible for the interaction between view and model. In our case will make the validation of the text, it doesn’t allow the view to add empty text into the list. Let’s make a new class named Controller. As an exercise add also a sort function.

The controller holds a reference to the model and provides functions to the view. In a more advanced implementation the Controller would listen for events from the view and the decides what action should do.

   1: package org.bserban.flex.simplemvc.controller
   2: {
   3:     import org.bserban.flex.simplemvc.model.ListModel
   4:     import mx.utils.StringUtil;
   5:     public class Controller
   6:     {
   7:        private var model:ListModel = ListModel.getInstance();
   8:         /**
   9:          * Glue the interaction between view and model.
  10:          */
  11:         public function Controller(){
  12:         }
  13:         /**
  14:          * add a new item into the list model.
  15:          */
  16:         public function addPerson(name:String):void
  17:         {
  18:             if(name==null || StringUtil.trim(name).length==0){
  19:                 trace("empty name");
  20:             }
  21:             else{
  22:                 model.addElement(name);
  23:            }
  24:         }
  25:     }
  26: }

View

The view is the graphical representation of the component. Usually it is a an mxml file but it can be also an actions script file for advanced programmers. It binds his data to the model and use the controller to process the view events. Let’s name it SimpleAirMVC.mxml. I added Air to its name because i decided to make an air project in Flex Builder.

   1: <?xml version="1.0" encoding="utf-8"?>
   2: <mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" height="226" width="235">
   3:     <mx:Script>
   4:         <![CDATA[
   5:         import mx.controls.List;
   6:         import org.bserban.flex.simplemvc.model.ListModel;
   7:         import org.bserban.flex.simplemvc.controller.Controller;
   8:
   9:         [Bindable]
  10:          private var model:ListModel = ListModel.getInstance();
  11:         private function clickHandler():void
  12:             {
  13:                 var controler:Controller = new Controller();
  14:                 controler.addPerson(personTxt.text);
  15:                 personTxt.text="";
  16:             }
  17:              ]]>
  18:     </mx:Script>
  19:
  20:     <mx:TextInput x="10" y="10" id="personTxt" text="" width="145"/>
  21:     <mx:Button x="163" y="10" label="Add" id="btnAdd" click="clickHandler()"/>
  22:     <mx:List x="10" y="40" width="218" dataProvider="{model.items}"></mx:List>
  23: </mx:WindowedApplication>

This a  simple way to implement a MVC in a small application. Things can become more complicated if the application is big. If this is the case then you should look over Pure MVC framework or Cairngorm. They eliminate the dependencies between MVC layer by using events. It is an event driven approach.

You may download the source code here SimpleAirMVC

Further reading

I recommend you to read the books ActionScript 3.0 Design Patterns and Advanced ActionScript 3 with Design Patterns