Flex之应用Cairngorm(4) - Cairngorm Extensions

Flex之使用Cairngorm(4) - Cairngorm Extensions
Previous Posts:
1.准备工作 http://nealmi.iteye.com/blog/164867
2.使用ModelLocator http://nealmi.iteye.com/blog/164879
3.Command & Event http://nealmi.iteye.com/blog/177370

Cairgorm Step By Step教程[推荐]
http://www.davidtucker.net/category/cairngorm/(英文)

下载源码:
后台指向我的Google App Engine 程序, 你可以暂时不关心后台, 直接导入到FlexBuilder里运行.
http://nealmi.iteye.com/topics/download/2e854ac3-89b2-3f15-814b-e4317380608e

就我个人来说,Cairngorm有两个致命的问题,直接影响到我是否使用它.
1.不支持通知视图.
Cairngorm2.1之前可以用ViewHelper 和 ViewLocator,但是自从Cairngorm2.1开始已经不推荐了.而且 ViewHelper和ViewLocator 方式本身就违反MVC.
2.不支持子Controller.

所以我选择了使用 UM Cairngorm Extensions. http://code.google.com/p/flexcairngorm/

Refactor To UM Cairngorm Extensions:

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>




6.注意Value Object的写法.要实现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.