Flex之使用Cairngorm(4) - Cairngorm Extensions

[b]Previous Posts:[/b]
1.準備工作 [url]http://nealmi.iteye.com/blog/164867[/url]
2.使用ModelLocator [url]http://nealmi.iteye.com/blog/164879[/url]
3.Command & Event [url]http://nealmi.iteye.com/blog/177370[/url]

Cairgorm Step By Step教程[color=red][推薦][/color]
[url]http://www.davidtucker.net/category/cairngorm/[/url](英文)

[b]下載源碼:[/b]
後臺指向我的Google App Engine 程序, 你可以暫時不關心後臺, 直接導入到FlexBuilder裏運行.
[url]http://nealmi.iteye.com/topics/download/2e854ac3-89b2-3f15-814b-e4317380608e[/url]

就我個人來說,Cairngorm有兩個致命的問題,直接影響到我是否使用它.
1.不支持通知視圖.
Cairngorm2.1之前可以用ViewHelper 和 ViewLocator,但是自從Cairngorm2.1開始已經不推薦了.而且 ViewHelper和ViewLocator 方式本身就違反MVC.
2.不支持子Controller.

所以我選擇了使用 UM Cairngorm Extensions. [url]http://code.google.com/p/flexcairngorm/[/url]

[b]Refactor To UM Cairngorm Extensions:[/b]

1.重構Event.繼承com.universalmind.cairngorm.events.UMEvent.
在構造函數裏接受一個IResponder類型的參數(用作通知視圖), UMEvent 本身帶有一個data屬性.

import com.universalmind.cairngorm.events.UMEvent;

public class LoginEvent extends UMEvent{

public static const LOGIN:String = "login";

public function LoginEvent(user:UserVO=null, callbacks:IResponder=null){
super(LOGIN, callbacks, true, true, user);
}

public function get user():UserVO{
trace("LoginEvent - user() - " + data);
return data as UserVO;
}


2.重構Controller.繼承com.universalmind.cairngorm.control.FrontController.增加對子Controller的支持,可以通過addSubController(..)方法來添加子Controller.這樣可以每個獨立的模塊有獨立MVC結構.

import com.universalmind.cairngorm.control.FrontController;


public class UserController extends FrontController {
public function UserController(){
super();
this.init();
}

private function init():void{
this.addCommand(LoginEvent.LOGIN, UserCommand);
//Add sub controller via addSubController(...);
}
}


3.重構Command.繼承com.universalmind.cairngorm.commands.Command.這裏通過用了一種可以減少類文件的寫法.(cairngorm繼承了JEE中大量的垃圾.類爆炸就是其中之一).

public class UserCommand extends Command{

override public function execute(event:CairngormEvent):void{
super.execute(event);

switch(event.type){
case LoginEvent.LOGIN:
doLogin(event as LoginEvent);
break;
case RegistrationEvent.REGISTRATION:
doRegistration(event as RegistrationEvent);
break;
default:
trace("Unkonw event type [" + event.type +"]");
}
}

private function doLogin(event:LoginEvent):void{
var delegate:UserDelegate = new UserDelegate(event.callbacks);
trace("LoginCommand - doLogin - " + event.user);
delegate.login(event.user);
}

private function doRegistration(event:RegistrationEvent):void{
var delegate:UserDelegate = new UserDelegate(event.callbacks);
trace("LoginCommand - doLogin - " + event.user);
//delegate.register(event.user);
}


3.重構Delegate, 繼承com.universalmind.cairngorm.business.Delegate.


public function UserDelegate(commandHandlers:IResponder=null){
//userService 聲明在Services.mxml裏.
super(commandHandlers, "userService");
}

public function login(user:UserVO):void {
trace("UserDelegate.login() - " + user);

//這裏多加了一層,你可以在這裏將服務器返回的結果加以處理,比如:將XML結果組裝成Value Object, 過濾掉某些數據等.
var token: AsyncToken = service.login(user.loginName, user.password);
var callbacks:Callbacks = new Callbacks(resultNotifyer, faultNotifyer);


prepareHandlers(token, callbacks);
}


private function resultNotifyer(event:ResultEvent):void{
//Alert.show(event.result + "","result");
trace("resultNotifyer - " + event );

// You can do something like filter data at here. eg: decode json .
// Code sample - Decode json:
// ------------------------------
// var rawData:String = event.result;
// var obj:Object = JSON.decode(rawData);
// var e:ResultEvent = new ResultEvent()
// e.resulte = obj;
// notifyCaller(e);

//通知視圖,也可以在Command裏執行.
notifyCaller(event);
}

private function faultNotifyer(event:FaultEvent):void{
//Alert.show(event.fault+"","fault");
trace("faultNotifyer - " + event );
notifyCaller(event);
}


6.View代碼.

<?xml version="1.0" encoding="utf-8"?>
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
import mx.utils.StringUtil;
import com.universalmind.cairngorm.events.Callbacks;
import net.imzw.UserManagerDemo.event.LoginEvent;
import mx.rpc.IResponder;
import mx.rpc.events.ResultEvent;
import mx.rpc.events.FaultEvent;
import net.imzw.UserManagerDemo.vo.UserVO;
import net.imzw.UserManagerDemo.model.UserManagerModelLocator;
import mx.controls.Alert;

private var modelLocator:UserManagerModelLocator = UserManagerModelLocator.getInstance();

private function login(e:MouseEvent):void{
//組裝Callback.
var callbacks:IResponder = new Callbacks(resultHandler, faultHandler);
var user:UserVO = new UserVO(StringUtil.trim(loginNameTextInput.text),
StringUtil.trim(passwordTextInput.text));
var loginEvent:LoginEvent = new LoginEvent(user, callbacks);
trace("doSignIn - " + loginEvent);

loginEvent.dispatch();
}
private function resultHandler(event:ResultEvent):void{

if(event.result == null){
Alert.show("登錄名或密碼錯誤.", "Error");

//登錄名或密碼錯誤時,設置焦點到用戶名TextInput,標準的Cairgorm很難做到指點.
loginNameTextInput.setFocus();
}else{
trace(event.result.loginName + "");
// Here should can simple like following code, but I got an error.
// May be case by fields mismatch between flex and backend.

// modelLocator.currentUser = event.result as UserVO;

var user:UserVO =new UserVO(event.result.loginName);
trace(user.loginName);
modelLocator.currentUser = user;
trace(modelLocator.currentUser.loginName);

modelLocator.workflowState = UserManagerModelLocator.MAIN_SCREEN;

reset();
}
}

private function faultHandler(event:FaultEvent):void{
trace(event.message + "");

Alert.show( "服務器錯誤, 請稍候再試.", "Error");
}

private function reset():void{
loginNameTextInput.text = "";
passwordTextInput.text = "";
}
]]>
</mx:Script>
<mx:Form defaultButton="{loginButton}" borderSides="left right top bottom" borderStyle="solid" borderColor="green">
<mx:FormHeading label="Please Login" />
<mx:FormItem label="LoginName">
<mx:TextInput id="loginNameTextInput" />
</mx:FormItem>
<mx:FormItem label="Password">
<mx:TextInput id="passwordTextInput" displayAsPassword="true"/>
</mx:FormItem>
<mx:HBox horizontalAlign="right" width="100%">
<mx:Button id="loginButton" click="{login(event)}" label="Login" />
</mx:HBox>
</mx:Form>
</mx:VBox>




[color=red]6.注意Value Object的寫法[/color].要實現com.universalmind.cairngorm.vo.IValueObject接口.實現copyFrom 和Clone方法.

package net.imzw.UserManagerDemo.vo{

import com.universalmind.cairngorm.vo.IValueObject;

[Bindable]
public class UserVO implements IValueObject{
public var id:Number;
public var loginName:String;
public var password:String;

public function UserVO( loginName:String=null, password:String=null ){
this.loginName = loginName;
this.password = password;
}

public function copyFrom(src:*):*{
this.loginName = src.loginName;
this.password = src.password;
}

public function clone():*{
return new UserVO(loginName, password);
}

public function equals(anotherUser:*):Boolean{
if(null == anotherUser) return false;

if(id == anotherUser.id && loginName == anotherUser.loginName){
return true;
}
return false;
}

public function toString():String{
return "User[loginName:"+loginName+"]";
}
}
}


以上只是粗略的介紹.有什麼問題可以聯繫我通過郵件 imzw.net+javaeye at gmail.com.

-------------
IT'S NEAL.
發佈了3 篇原創文章 · 獲贊 0 · 訪問量 2053
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章