管理员登录 现在我们看看登录程序中最核心的部分,也就是LoginWindow类的实现。首先使用Ext.extend(Ext.Window,{})表示继承Ext.Window类,后面大括号中的参数是指子类中的方法及属性等,由于LoginWindow类继承了Ext.Window,因此他就拥有了Ext.Window类的所有特性及功能,我们只需要给LoginWindow添加上具体的行为及特性即可,这个行为及特性出就是参数{}中定义的内容。下面我们来看看给LoginWindow类增加了哪些方法及属性。 然后接着为LoginWindow类定义了两个方法,第一个是createFormPanel,主要用来创建窗口中登录表单面板及具体的输入框等内容,代码及注释如下: 接下来定义LoginWindow类的第二个方法login,该方法是当用户点打击登录按钮时执行的代码,主要用来往服务器端发送数据,并根据服务器的返回情况,执行相应的功能,login方法中只有一句代码,即this.fp.form.submit({…}),直接调用表单面板fp上的表单位提交方法,执行表单提交操作,全部代码及注释如下: 到这里,关于adminLogin.js的整个代码就讲解完了,你执行一下login.html,就能看到这个比较酷的登录表单效果了。
管理员登录我们使用到了ExtJS,进入页面为login.html,内容如下所示:
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>登录系统</title>
<link rel="stylesheet" type="text/css" href="plugins/extjs/ext-2.0/resources/css/ext-all.css" />
<link rel="stylesheet" type="text/css" href="stylesheet/ext-patch.css" />
<style type="text/css">
.user{ background:url(images/user.gif) no-repeat 1px 2px; }
.key{ background:url(images/key.gif) no-repeat 1px 2px; }
.key,.user{
background-color:#FFFFFF;
padding-left:20px;
font-weight:bold;
color:#000033;
}
</style>
</head>
<body>
<div id="loading-mask" style="">
<div id="loading">
<div style="text-align:center;padding-top:26%"><img src="images/extanim32.gif" width="32" height="32" style="margin-right:8px;" align="absmiddle"/>Loading...</div>
</div>
</div>
<script type="text/javascript" src="plugins/extjs/ext-2.0/adapter/ext/ext-base.js"></script>
<script type="text/javascript" src="plugins/extjs/ext-2.0/ext-all.js"></script>
<script src="javascript/adminLogin.js" type="text/javascript"></script>
</body>
</html>
我们来重点看下面几个以ExtJS有关的代码:
首先在<head>中使用<link rel="stylesheet" type="text/css" href="plugins/extjs/ext-2.0/resources/css/ext-all.css" />引入ExtJS的样式定义文件。
然后在<body>的开始使用了一个id为<div id="loading-mask">中的内容用于显示下面Loading图标的内容:
接下来的代码是使用<script>引入该登录应用中涉及到的内个js文件,包括ext-base.js、ext-all.js这两个ExtJS框架的库文件,要使用ExtJS,就需要引入这两个库,当然如果你使用其它适配器,也可以引入类似ext-prototype-adapter.js这样的文件代替ext-base.js;然后引入adminLogin.js这个文件,该文件是管理员登录应用中使用到的js文件,也是该登录应用核心部分,下面我将会通过一小节来对adminLogin.js作详尽的讲解。
adminLogin.js代码及讲解
adminLogin.js的目的在于显示一个登录表单,并在用户点击表单上的按钮时,向服务器端发送请求执行登录验证操作,如果登录成功,则导向后台管理主页面。要显示的登录表单如下图所示:
打开adminLogin.js,该文件一共包含四句代码。我们首先会看到下面的代码:
Ext.BLANK_IMAGE_URL = '../plugins/extjs/ext-2.0/resources/images/default/s.gif';
Ext.QuickTips.init();//初始化鼠标停留时的显示框
第一句用来指定空白图片的URL,由于该属性的默认值为http://extjs.com/s.gif,这样访问起来会有点慢;第二句代码用来初始化Ext的快速提示信息,这在我们提供表单按钮等验证的时候,Ext显示验证信息的时候其组件会用到。
然后第三句代码是LoginWindow=Ext.extend(Ext.Window,{…});用来定义一个继承Ext.Window自定义窗口控件,从图可以看出,登录表单是一个带阴影的立体窗口,比传统的表单酷多了;由于把整个表单定义成了一个自定义的Ext控件类,因此在其它任何地方,我们想显示登录表单的时候,都可以直接new 一个LoginWindow的对象即可。
adminLogin.js中的最后一句代码是Ext.onReady(function(){…});onReady是Ext类的一个全局静态方法,表示在文档html内容加载完后,DOM的onload方法执行及图片加载完之前执行要执行的函数。在这里我们是一个用function(){}定义的匿名函数,函数体的内容如下:
var win=new LoginWindow();
win.show();
setTimeout(function() {
Ext.get('loading-mask').fadeOut( {
remove : true
});
}, 300);
首先使用new LoginWindow()来初始化一个登录窗口对象,并赋值给变量win,然后使用win.show()方法来显示这个窗口,最后一句是用来把login.html页面中用来显示加载信息的div从当前文档中删除掉。setTimeout 这个大家应该都知道,表示在多少毫秒后执行某一个函数,这里是一个匿名函数作为参数,匿名函数中的Ext.get('loading-mask')方法得到与文档中id为loading-mask的div对应的元素Element,然后执行元素的fadeOut方法实现显示一个淡出的渐变效果,参数remove:true表示效果执行完后把这个元素删除掉。
首先,最前面的几个属性内容及说明分别如下:
title : '登陆系统', //表示该窗口的标题默认显示为'登陆系统'
width : 265, //表示该窗口的默认宽度为265象素
height : 140, //表示该窗口的默认高度为140象素
collapsible : true,//表示该窗口可以收缩起来
defaults : { //表示该窗口中所有子元素的特性
border : false //表示所有子元素都不要显示边框
},
buttonAlign : 'center', //表示该窗口的按钮为居中对齐方式
createFormPanel :function() {
//直接使用new方法创建一个表单面板并返回
return new Ext.form.FormPanel( {
// 给面板的body元素指定自定义的CSS样式信息
bodyStyle : 'padding-top:6px',
// 表单面板中元素的默认类型
defaultType : 'textfield',
// 字段标签的默认对齐方式为右对齐
labelAlign : 'right',
// 指定标签的默认长度
labelWidth : 55,
// 标签与字段录入框之间的空白
labelPad : 0,
// 窗口是否显示背景色
frame : true,
// 表单面板容器中组件默认统一配置选项
defaults : {
// 验证字段是否能为空,这里为不允许为空
allowBlank : false,
// 字段默认宽度为158象素
width : 158
},
// 指定表单面板容器中的元素,前面已经定义了默认为textfield
items : [{
// 给元素添加自定义的CSS样式user
cls : 'user',
// 字段的名称
name : 'userName',
// 指定字段的标签
fieldLabel : '帐号',
// 为空时提示信息
blankText : '帐号不能为空'
}, {
// 给元素添加自定义的CSS样式key
cls : 'key',
// 字段的名称
name : 'password',
// 指定字段的标签
fieldLabel : '密码',
// 为空时提示信息
blankText : '密码不能为空',
// 指定字段的输入方式为password,也就输入时不会出现明文
inputType : 'password'
}]
});
}
login:function() {
// 执行当前表单面板的submit
this.fp.form.submit({//定义submit的参数选项
// 动作发生期间默认等待对话框中显示的文本信息
waitMsg : '正在登录......',
// submit发生时指向的地址
url : 'portal.ejf?cmd=adminLogin',
// 服务器端返回成功标注信息时,也就是验证通过时发生的动作
success : function(form, action) {
window.location.href = 'manage.ejf';//直接跳转到manage.ejf,也就是后台管理主程序
},
// 反之,如果登录验证失败,则执行failure中的指定的函数
failure : function(form, action) {
form.reset();//重置表单的内容,也就是把表单的reset一下
if (action.failureType == Ext.form.Action.SERVER_INVALID)
Ext.MessageBox.alert('警告', action.result.errors.msg);//如果是服务器端验证错误,也就是服务器返回false,则使用Ext.MessageBox的alert函数显示一个漂亮的验证错误信息提示框,验证信息的内容直接从服务器返回的result中读取
}
});
}
LoginWindow类的最后还有一个方法,也是Ext控件中非常重要的方法,即initComponent。该方法是Ext控件初始化时执行的方法,所有Ext可视化组件都包含该方法,每一个子类想添加自定义的控件信息,都可以重新定义该方法,但需要注意的一点是子类中重新定义的initComponent一定要调用父类的initComponent方法。下面LoginWindow方法的源代码:
initComponent : function(){
LoginWindow.superclass.initComponent.call(this);
this.fp=this.createFormPanel();
this.add(this.fp);
this.addButton('登陆',this.login,this);
this.addButton('重置', function(){this.fp.form.reset();},this);
}
我们可以看到, initComponent的第一句LoginWindow.superclass.initComponent.call(this)就是直接调用子类的方法,你可以理解为像java中在子类构造函数中使用super()直接调用基类构造函数。
第二行语句直接调用前台定义的createFormPanel方法,来获得一个表单面板对象,交赋值给LoginWindow对象的fp属性。然后使用this.add(this.fp)可以把该表单面板添加到窗口内容区;接着是调用this.addButton方法来在窗口中添加两个按钮。addButton是从父类Ext.Window的父类Ext.Panel(相当于爷爷类)继承来的。第一个参数表示按钮显示的名称,第二个参数表示点击该按钮时执行的回调函数,第三个参数表示回调函数执行的作用域。如果你对“回调函数”,“作用域”这些词不理解的话,还请赶紧补习javascript的基础知识。从代码中我们可以看到,登录按钮的回调函(也可以是事件响应函数或者事件处理器)数是this.login方法,也主前面定义的login方法,所以我们只要一点该按钮就会执行登录操作;而重置按钮的事件响应函数是一个匿名函数,匿名函数体的内容就是一句代码,即:this.fp.form.reset(),表示执行窗口中当前表单的重置操作。
管理员登录后台
在讲完了前台静态页面及相应js中的内容以后,现在来看看用于处理管理员登录的后台Java代码。
在前面的adminLogin.js的login方法中,提交表单时指定的url为portal.ejf?cmd=adminLogin,也主是说管理员登录验证由portal模块(PortalAction)中的adminLogin命令方法来处理,其代码如下:
public class PortalAction extends AbstractPageCmdAction {
private IBlogService blogService;
public Page doAdminLogin(WebForm form) {
String name=CommUtil.null2String(form.get("userName"));
boolean ret = blogService.adminLogin(name, CommUtil.null2String(form.get("password")));
if (ret) {
ActionContext.getContext().getSession().setAttribute("ADMIN","true");
} else {
this.addError("msg", "用户名或密码错误,请重新登录!");
}
return pageForExtForm(form);
}
}
后台代码直接通过form.get("userName")及form.get("password")得到用户名及密码,然后调用IBlogService(即blogService)中的adminLogin方法来验证管理登录信息。如果返回true,则表示验证通过,直接通过ActionContext来获得Session对象,并保存一个名称为ADMIN,值为TRUE的内容在Session中。如果登录出错,则直接使用 this.addError方法把错误信息保保存到当前上下文中。由于该Action是直接使用ExtJS来调用的,因此我们在方法的最后直接使用return pageForExtForm(form)方法来返回一个专为ExtJS Form的发起的数据请求提供数据的page。
再来玩一下LoginWindow
前面不是说了LoginWindow是一个类吗,那么没有什么特殊情况(比如不是单例类)那么我们应该可以创建多个LoginWindow的实例了,Ext是基于面向对象编程的,很多组件的设计思想跟Java Swing中的组件设计非常相似。Javascript是一个面向对象的动态语言,可别小看他了。下面我们放松一下,把下面的代码添加到adminLogin.js中的Ext.onRead匿名函数参数的函数体内部,如下所示:
Ext.onReady(function()
{
for(var i=0;i<20;i++)
{
var win=new LoginWindow({title:"登录窗口"+i,x:(i*30)});
win.show();
}
setTimeout(function() {
Ext.get('loading-mask').fadeOut( {
remove : true
});
}, 300);
}
);
再刷新一下login.html,你会得到类似下图所示的效果:
哇,好多窗口啊,每个都有自己的标题,得来全不费工夫。你可拖动这些窗口,在每一个窗口中登录,执行各自的重置操作等,如下图所示:
你可以通过传递配置参数title及x来改变窗口的标题及初始位置,当然还有其它更多有趣的东东,你得慢慢去发掘,这应该也是面向对象的好处。