The importance of using a presentation model for keeping your flex pages clean of code and your backend methods reactionary.
Here is an example of a typical flex file.
<?xml version=“1.0” encoding=“utf-8”?>
<af:AdminResourceBase xmlns:mx=“http://www.adobe.com/2006/mxml"
xmlns:appcore=“http://www.wookets.com/flex"
creationComplete=“setup()”
clipContent=“false” label=“Add”>
<!– control –>
<mx:Script>
<![CDATA[
import com.wookets.query.dto.Find;
import appcore.query.Tide;
import com.wookets.core.model.User;
import com.wookets.track.model.AppAward;
import mx.collections.ArrayCollection;
private function setup():void {
loadUsers();
}
public override function beforeSave():void {
b_add.enabled = false;
b_add.label = “adding…”;
}
public override function afterSave():void {
super.afterSave();
b_add.enabled = true;
b_add.label = “add”;
}
private function loadUsers():void {
Tide.findAll(new Find(User).orderBy(“name”),
function(result:Array):void {
f_user.dataProvider = result;
});
}
]]>
</mx:Script>
<!– view –>
<mx:Form verticalGap=“20” width=“100%” borderStyle=“solid”>
<mx:FormItem>
<mx:Label text=“Award Title”/>
<appcore:TextInput id=“f_name” bindTo=“sr.name”
width=“300” minLength=“3” maxLength=“120”/>
</mx:FormItem>
<mx:FormItem>
<mx:Label text=“Award Key”/>
<appcore:TextInput id=“f_keyname” bindTo=“sr.awardKey”
width=“300” minLength=“3” maxLength=“120”/>
</mx:FormItem>
<mx:FormItem>
<mx:Label text=“User Granted the Award”/>
<appcore:ComboBox id=“f_user” bindTo=“sr.user” labelField=“name”/>
</mx:FormItem>
<mx:FormItem direction=“horizontal”>
<appcore:CheckBox id=“f_active” bindTo=“sr.active”/>
<mx:Text text=“Show this resource on the website immediately after it is created?”/>
</mx:FormItem>
<mx:FormItem>
<appcore:Button id=“b_add” label=“add” click=“save()” enableWhenValid=“{[f_name,f_keyname]}“/>
</mx:FormItem>
</mx:Form>
</af:AdminResourceBase>
Which I will tell you is not too bad… However, assigning ‘id’ to some components and having the methods rely on the view can really create havoc. What happens when you change the view or redesign something? What if a rogue prosigner decides to rewrite your admin pages?
A better / cleaner separation of concerns can be found using the presentation model pattern (see references below).
Here is a view I just wrote using the presentation model design pattern. First the UI;
<?xml version=“1.0” encoding=“utf-8”?>
<s:Group xmlns:fx=“http://ns.adobe.com/mxml/2009" xmlns:s=“library://ns.adobe.com/flex/spark”
xmlns:mx=“library://ns.adobe.com/flex/mx” currentState=“@{pm.currentState}”>
<fx:Script>
<![CDATA[
import view.pm.GoalsViewPM;
[Bindable] [Inject] public var pm:GoalsViewPM;
]]>
</fx:Script>
<s:states>
<s:State name=“list”/>
<s:State name=“edit”/>
<s:State name=“new”/>
</s:states>
<s:VGroup height=“100%” width=“100%”>
<s:HGroup width=“100%”>
<s:TextInput width=“100%” prompt=“Search…” change=“{pm.search(event.currentTarget.text)}”/>
</s:HGroup>
<mx:DataGrid dataProvider=“{pm.session.goalsList}” height=“100%” width=“100%”
change=“{pm.changeSelected(event.currentTarget.selectedItem)}”>
<mx:columns>
<mx:DataGridColumn dataField=“name” headerText=“”/>
</mx:columns>
</mx:DataGrid>
<s:VGroup width=“100%” includeIn=“edit,new”>
<s:Form>
<s:FormHeading label.edit=“Editing {pm.selectedGoal.name}” label.new=“Creating…”/>
<s:FormItem>
<s:TextInput />
</s:FormItem>
</s:Form>
</s:VGroup>
</s:VGroup>
</s:Group>
And the backend AS3 code;
package view.pm {
import actifi.query.QueryService;
import com.temp.model.SmartGoal;
import com.wookets.query.dto.Find;
import core.context.SessionContext;
import mx.collections.ArrayCollection;
public class GoalsViewPM {
[Bindable] public var currentState:String;
[Bindable] public var selectedGoal:SmartGoal;
[Inject]
[Bindable] public var session:SessionContext;
[Inject]
public var query:QueryService;
[PostConstruct]
public function setup():void {
if (session.goalsList == null) {
var find:Find = new Find().from(SmartGoal);
query.findAll(find).onResult(function(result:Array):void {
session.goalsList = new ArrayCollection(result);
session.goalsList.filterFunction = function(item:Object):Object {
return item.name.match(new RegExp(searchString, ‘i’));
}
});
}
}
private var searchString:String = “”;
public function search(input:String):void {
searchString = input;
session.goalsList.refresh();
}
public function changeSelected(value:SmartGoal):void {
selectedGoal = value;
if(selectedGoal != null) {
currentState = “edit”;
} else {
currentState = “list”;
}
}
}
}
More code right? Yes… And the code is in two different files… Pretty annoying, but not when you think about it… The view is highly decoupled from the control. Yes, the view utilizes and makes references to methods and models of the control, but we can’t really get around that… The control actually has no reference what so ever to the view. Your local rogue prosigner can change the View (mxml) to his / her hearts content and it won’t mess up your control. The view always reacts to the control. The control never changes when the view changes (unless you’re making the view richer, in which case you are always adding things and never changing existing functionality).
This is also helpful when writing tests… Tests can run against your controls without a care for a view being present.
The fact that there are no more ids being assigned to components or trying to decide which way to code… A binding, an id, use the event? Just use the PM and you’re done.
(sorry for the lack of an example project)
References:
http://swizframework.org/
http://swizframework.jira.com/wiki/display/SWIZ/Presentation+Model