Good Tutorial for learning PureMVC with Sample Code:-
(url: http://codeofdoom.com/wordpress/2009/01/25/puremvc-sample/ )
Well it has been awhile since I have written anything, but that doesn’t mean that I haven’t been busy. Lately at work I’ve been working a lot more closely with Flex, writing some pretty creative/new custom components, learning a lot, etc. Lately we discussed the possibilities of switching from the standard use of Cairngorm to the use a PureMVC, so I wanted to go over a brief tutorial of PureMVC to show how its used. Later I will be posting a short article over the differences of cairngorm vs PureMVC.
First, what is PureMVC? It’s an open source MVC (Model, View, Component) framework that has now been ported over to 10 different languages (latest has been to Objective C for iPhone dev) that really forces you follow the rules of MVC. It allows you to separate out all of your logic of retrieving your data, for what to do with that data, what components get what, etc all through one central facade object. Now this tutorial will be focusing on how it works with the singleton core, but they also have the notion of a “multiton”, which is basically a map of singletons.
Quick run down of the 4 parts.
- Proxies are used to manage your data object. It will be the one that requests your data. Once the data returns, it will then fire off a notification stating that the data has returned.
- Mediators are used to managed your components. This also picks up notifications of data and that is where you will set the data to your components.
- Commands are mostly there to interact with the Proxies and Mediators
- Facade is the single interface (also are a singleton) used to manage the communications of your application
So lets start diving into some code. In this example, I am basically using a HTTPService to load an rss feed into a datagrid.
First things first, we grab an instance of our facade and pass in the components we want to register with it. This is within the PureMvcExample.mxml file.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| <?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="creationComplete()">
<mx:Script>
<![CDATA[
import mx.controls.DataGrid;
import mx.controls.ComboBox;
import mx.controls.Label;
import com.codeofdoom.puremvc.facade.MyFacade;
public function creationComplete():void{
var facade:MyFacade = MyFacade.getInstance();
facade.startup(grid);
}
]]>
</mx:Script>
<mx:DataGrid id="grid" width="80%">
<mx:columns>
<mx:DataGridColumn dataField="title"/>
<mx:DataGridColumn dataField="link"/>
<mx:DataGridColumn dataField="description"/>
</mx:columns>
</mx:DataGrid>
</mx:Application>
|
When you first initialize the facade, you will notice there is an initializeController method called whenever you do super() in the constructor. This is where it will register your command to start the application.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
| package com.codeofdoom.puremvc.facade
{
import com.PureMvcConstants;
import com.codeofdoom.puremvc.command.StartUpCommand;
import org.puremvc.as3.interfaces.IModel;
import org.puremvc.as3.patterns.facade.Facade;
import org.puremvc.as3.patterns.observer.Notification;
public class MyFacade extends Facade
{
public function MyFacade()
{
super();
}
public static function getInstance():MyFacade {
if (instance == null) {
instance = new MyFacade( );
}
return instance as MyFacade;
}
public function startup(app:Object):void{
notifyObservers(new Notification(PureMvcConstants.STARTUP,app));
}
override protected function initializeController():void {
super.initializeController();
registerCommand(PureMvcConstants.STARTUP, StartUpCommand);
}
}
}
|
As you see, the StartUpCommand just extends Command and is used to registered both the proxies and the mediators with the facade. Nothing too special.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
| package com.codeofdoom.puremvc.proxy
{
import com.PureMvcConstants;
import com.codeofdoom.puremvc.facade.MyFacade;
import flash.net.URLRequest;
import flash.utils.Proxy;
import mx.rpc.events.ResultEvent;
import mx.rpc.http.HTTPService;
import org.puremvc.as3.interfaces.IProxy;
import org.puremvc.as3.patterns.observer.Notification;
public final class MyProxy extends Proxy implements IProxy
{
public static const NAME:String = "com.codeofdoom.puremvc.MyProxy";
private var _service:HTTPService;
private var _xml:XML;
public function MyProxy(data:Object = null){
super();
setup();
}
private function setup():void{
_service = new HTTPService();
_service.resultFormat="e4x";
_service.url = "http://feedproxy.google.com/IdeaExcursion";
_service.addEventListener(ResultEvent.RESULT,onLoad);
}
private function onLoad(e:ResultEvent):void{
var facade:MyFacade = MyFacade.getInstance();
facade.notifyObservers(new Notification(PureMvcConstants.DATA_LOADED,e.result));
}
public function getProxyName():String{
return NAME;
}
public function loadInfo():void{
_service.send();
}
}
}
|
When we initialize MyProxy, you will notice that it will call setup(). This is what we use to setup the service call. We add an eventlistener to it for when data comes back and thats where some fun stuff goes on. Now that our proxy is initialized, the next line in the StartUpCommand is to initialize the mediator.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
| package com.codeofdoom.puremvc.mediator
{
import com.PureMvcConstants;
import com.codeofdoom.puremvc.proxy.MyProxy;
import mx.controls.DataGrid;
import org.puremvc.as3.interfaces.IMediator;
import org.puremvc.as3.interfaces.INotification;
import org.puremvc.as3.patterns.mediator.Mediator;
public class MyMediator extends Mediator implements IMediator
{
public static const NAME:String = "com.codeofdoom.puremvc.mediator.MyMediator";
public function MyMediator(viewComponent:Object=null)
{
super(NAME, viewComponent);
loading();
}
override public function getMediatorName():String
{
return NAME;
}
override public function getViewComponent():Object
{
return viewComponent;
}
override public function listNotificationInterests():Array
{
return [PureMvcConstants.DATA_LOADED];
}
override public function handleNotification(notification:INotification):void
{
switch (notification.getName()){
case PureMvcConstants.DATA_LOADED:
var xml:Xml = notification.getBody().channel.item;
(viewComponent as DataGrid).dataProvider = xml;
break;
default: viewComponent.text = "notification name not found";
}
}
public function loading():void{
MyProxy(facade.retrieveProxy(MyProxy.NAME)).loadInfo();
}
}
}
|
Now whenever we register a mediator, within PureMVC, it will call the listNotificationInterests method. This is what tells PureMVC which events that this specific mediator cares about. For our example, we only care about PureMvcConstants.DATA_LOADED. Inside the constructor, we then call loading that calls MyProxy to go retrieve the data. Note that we are receiving the proxy through the facade. Typically you would have an interface that you would cast this to in a real application and not cast MyProxy directly, but this is just an example.
So lets look back at MyProxy.
1
2
3
4
| private function onLoad(e:ResultEvent):void{
var facade:MyFacade = MyFacade.getInstance();
facade.notifyObservers(new Notification(PureMvcConstants.DATA_LOADED,e.result));
}
|
The loadInfo() just has a _service.send() call. After that our eventlistener that we setup before picks it up. All that does is just takes the data uses the facade to fire off the notification that “here is some data” (under the fancy name PureMvcConstants.DATA_LOADED) . But who is listening?
Recall earlier that our mediator listed PureMvcConstants.DATA_LOADED as an “interest”.
1
2
3
| override public function listNotificationInterests():Array{
return [PureMvcConstants.DATA_LOADED];
}
|
So if you go back to MyMediator, there is a handleNotificationMethod that we have overridden.
1
2
3
4
5
6
7
8
| override public function handleNotification(notification:INotification):void{
switch (notification.getName()){
case PureMvcConstants.DATA_LOADED:
var xml:Xml = notification.getBody().channel.item;
(viewComponent as DataGrid).dataProvider = xml;
break;
}
}
|
This is where we take our data back that was sent off with the notification and apply it to our viewComponent.
You might not notice it, but I feel the mediator level is what really makes PureMvc special. We have now separated out application specific code into their own class. With this extra layer, if you wanted to swap out what happens when data comes back in the application, all you have to do is change the mediator. You dont have to worry about writing that code within the viewComponent itself anymore.
Now this is a small example of how the flow of a PureMvc application works, but normally you would pass in a higher level component as your viewComponent into the mediator. Something that will contain 1 or more actual components within it. If you didnt take that route, then you are talking about having proxys and mediators for everything. I suppose that is doable, but it is probably not recommended.