Skip to content
soundstep edited this page Feb 18, 2011 · 21 revisions

Create a model class

The models are classes used to manage you application’s data model.

The data can be XML, local data, data retrieved from a server, arrays or anything. Ideally, the data should be set to the data property of the model instance, but you are free to create specific getters and variables.

You can create as many models as you need. Your model class must extends Model and implements IModel.

Two methods can be overridden for conveniency, the initialize method will be called once a model has been added to the framework and the dispose method will be called once a model has been removed from the framework. The initialize method can be used as a starting point to create or load your data and the dispose method to destroy the data so it can be garbage collected.

It is important that your model has no reference to the framework so it stays decoupled from the rest of the application. Your application will be built in an efficient loose-coupling way making it easily maintainable.

The name of a model is only necessary to register it to the framework if injection is not used.

package  {
    import com.soma.core.interfaces.IModel;
    import com.soma.core.model.Model;
    
    public class ModelExample extends Model implements IModel {
        
        public static const NAME:String = "APP::ModelExample";
        
        public function ModelExample() {
            super(NAME);
        }
        
        override protected function initialize():void {
            // called when the model has been registered to the framework
            data = <data><node id="myData"/></data>;
        }
        
        override protected function dispose():void {
            // called when the model has been removed from the framework
            data = null;
        }
        
    }
}

Register a model class

There are two ways to register a model to the framework and this can be done in four places:

  • in the framework instance
  • in a wire instance
  • in a mediator instance
  • in a command instance

In either place, a model can be registered this way:

addModel(ModelExample.NAME, new ModelExample());

Retrieve a model class

The model instances registered to the framework can easily be retrieved to use its data in the same four places.

var model:IModel = getModel(ModelExample.NAME);
var modelData:XML = getModel(ModelExample.NAME).data as XML;
var modelExample:ModelExample = getModel(ModelExample.NAME) as ModelExample;

Remove a model class

A model class that is not used anymore can be removed in the same four places. When a model gets removed, the framework will automatically call its dispose method and set to null in order to be garbage collected.

removeModel(ModelExample.NAME);

Test if a model instance has been registered

if (hasModel(ModelExample.NAME)) {
    // do something
}

Shortcuts when injection is not used

In wire instances, it is a good practice to create getter shortcuts. Here are some examples:

private function get modelExample():ModelExample {
    return ModelExample(getModel(ModelExample.NAME));
}
private function get modelData():XML {
    return ModelExample(getModel(ModelExample.NAME)).data as XML;
}

They can now be used directly from another place:

override protected function initialize():void {
    trace(modelExample.getName());
    trace(modelData.toXMLString());
}

Model using injection

Injection makes it even easier to register and retrieve a model. A model that will be injected will have the same form beside the name that is not needed, unless you wish to both use injection and register it to the framework registry.

package  {
    import com.soma.core.interfaces.IModel;
    import com.soma.core.model.Model;
    
    public class ModelExample extends Model implements IModel {
        
        override protected function initialize():void {
            // called when the model has been registered to the framework
            data = <data><node id="myData"/></data>;
        }
        
        override protected function dispose():void {
            // called when the model has been removed from the framework
            data = null;
        }
        
    }
}

Register a model class to the injector

They are many ways to register a model to the framework using injection depending of the behavior wanted when it gets injected.

Register as a unique instance

If only one instance of a model class must be created, the model can be registered using the injector.mapSingleton() method. The name Singleton doesn’t mean that it is a real Singleton. It means that only one instance will be created when needed, making a good of lazy instantiation, and that the same instance will be injected every time it is asked.

injector.mapSingleton(ModelExample);

This line means: every time I ask for a ModelExample to be injected, create a new instance of the ModelExample if it is the first time I ask. Otherwise, inject the instance that has been already created.

An instance can be created before being injected, if needed, using the injector.getInstance() method.

var model:ModelExample = injector.getInstance(ModelExample) as ModelExample;

Register as multiple instances

If a model requires to be a new instance every time it gets injected, the method injector.map() can be used.

injector.map(ModelExample, ModelExample);

This line means: every time I ask for a ModelExample to be injected, create a new instance of the ModelExample class.

Register an instance

A model can also be instantiated and register to the injector. Every time, the model class must be injected, this precise instance will be used for the injection.

var model:ModelExample = new ModelExample();
injector.mapToInstance(ModelExample, model);

This line means: every time I ask for a ModelExample to be injected, use the model instance that has been registered.

Unregister a model class from the injector

A model class or instance can be unregistered (unmapped) from the injector using the injector.removeMapping() method.

injector.removeMapping(ModelExample);

Inject a Model class

To inject a model instance in another class, the injector will need to find a [Inject] metadata tag in the injectee class.

To perform an injection, the injectee class must be created by the injector, or manually with the injector.injectInto() method.

Here is a simple example of a model that get injected in a wire.

// mapping rule
injector.mapSingleton(ModelExample);

// create a wire instance to inject the model into
injector.getInstance(WireExample);
// or
var wire:WireExample = new WireExample();
injector.injectInto(wire);

package  {
    import com.soma.core.wire.Wire;
    import com.soma.core.interfaces.IWire;
    
    public class WireExample extends Wire implements IWire {
        
        [Inject]
        public var model:ModelExample;
        
        override public function initialize():void {
            // called when the wire has been registered to the framework
            trace("model injected:", model);
        }
        
        override public function dispose():void {
            // called when the wire has been removed from the framework
        }
        
    }
}

Connections with the framework

A model instance doesn’t have a direct access to the framework instance, or to other methods that can be found in wires and commands. However, a model instance can listen to events and commands and also dispatch events and commands to the framework.

A model instance makes use of handy shortcuts to provide the same API as Flash for built-in events: addEventListener, dispatchEvent, and so on.

A dispatcher (IEventDispatcher) can be registered to a model instance (dispatcher property), the default dispatcher is the framework instance itself, but it is not a good pratice to cast it to this framework instance. It is a better pratice to dispatch some events to tell the application that this model has done its job, that the data is ready or has changed, and that this event/command can be used everywhere in the application.

The following example demonstrates how a command could be used to load some data, and another to dispatch its result. Note that the way the model listen to an event is just an example as it is possible to do so, but a command instance calling a requestData public method on this model would be a better practice.

package  {
    import com.soma.core.interfaces.IModel;
    import com.soma.core.model.Model;
    
    public class ModelExample extends Model implements IModel {
        
        override protected function initialize():void {
            addEventListener(MyEvent.REQUEST_DATA, requestData);
        }

        private function requestData(event:MyEvent):void {
            // load or create some data
            data = <data><node id="myData"/></data>;
            // dispatch that the data is ready
            dispatchEvent(new MyEvent(MyEvent.REQUEST_DATA_COMPLETE));
        }
        
        override protected function dispose():void {
            removeEventListener(MyEvent.REQUEST_DATA, requestData);
        }
        
    }
}

It is a good practice and good application flow for this command (REQUEST_DATA_COMPLETE) to be listened to in other framework elements. For example, in several wires and mediators to update views or other elements after that the data has been changed.

Here is an example in a mediator where a model gets injected.

package {
    import com.soma.core.interfaces.IMediator;
    import com.soma.core.mediator.Mediator;
    public class MyViewMediator extends Mediator implements IMediator {
        
        [Inject]
        public var view:MyView;
        
        [Inject]
        public var model:ModelExample;
        
        override public function initialize():void {
            addEventListener(MyEvent.REQUEST_DATA_COMPLETE, dataCompleteHandler);
        }
         
        private function dataCompleteHandler(event:MyEvent):void {
            view.update(model.data);
        }
        
        override public function dispose():void {
            removeEventListener(MyEvent.REQUEST_DATA_COMPLETE, dataCompleteHandler);
        }
        
    }
}