Ext2.0是一个JS框架,它的Grid控件和其它可以显示数据的控件,能够支持多种数据类型,如二维数组、Json数据和XML数据,甚至包括我们自定义的数据类型。Ext为我们提供了一个桥梁Ext.data.Store,通过它我们可以把任何格式的数据转化成grid可以使用的形式,这样就不需要为每种数据格式写一个grid的实现了。
首先,一个表格应该有列定义,即定义表头ColumnModel:
// 定义一个ColumnModel,表头中有四列
var cm = new Ext.grid.ColumnModel([
{header:'编号',dataIndex:'id'},
{header:'性别',dataIndex:'sex'},
{header:'名称',dataIndex:'name'},
{header:'描述',dataIndex:'descn'}
]);
cm.defaultSortable = true;
该ColumnModel定义了表格的四个列,其每列的名称和对应的数据键。请注意defaultSortable属性,即为每个列都安上一个可以排序的功能。如果只想某些列举有该功能,可以设置:
{header:'编号',dataIndex:'id',Sortable:true},
现在就来看看这个Ext.data.Store是如何转换三种数据的。
1.二维数组:
// ArrayData
var data = [
['1','male','name1','descn1'],
['2','male','name1','descn2'],
['3','male','name3','descn3'],
['4','male','name4','descn4'],
['5','male','name5','descn5']
];
// ArrayReader
var ds = new Ext.data.Store({
proxy: new Ext.data.MemoryProxy(data),
reader: new Ext.data.ArrayReader({}, [
{name: 'id',mapping: 0},
{name: 'sex',mapping: 1},
{name: 'name',mapping: 2},
{name: 'descn',mapping: 3}
])
});
ds.load();
ds要对应两个部分:proxy和reader。proxy告诉我们从哪里获得数据,reader告诉我们如何解析这个数据。
现在用的是Ext.data.MemoryProxy,它将内存中的数据data作为参数传递。Ext.data.ArrayReader专门用来解析数 组,并且告诉我们它会按照定义的规范进行解析,每行按顺序读取四个数据,第一个叫id,第二个叫sex,第三个叫name,第四个descn。这些是跟 cm定义中的dataIndex对应的。这样cm就知道哪列应该显示那条数据了。
mapping属性用于标记data中的读取后的数据与标头的映射关系,一般是不用设置的。但如果我们想让sex的数据中name栏中出现,可以设置mapping值。即id的mapping为2,后者为0。
记得要执行一次ds.load(),对数据进行初始化。
数据的显示显得非常简单:
HTML文件:
<div id='grid'></div>
JS文件:
var grid = new Ext.grid.GridPanel({
el: 'grid',
ds: ds,
cm: cm
});
grid.render();
其显示结果为:
2.如何在表格中添加CheckBox呢?
var sm = new Ext.grid.CheckboxSelectionModel();
var cm = new Ext.grid.ColumnModel([
new Ext.grid.RowNumberer(),//自动行号
sm,//添加的地方
{header:'编号',dataIndex:'id'},
{header:'性别',dataIndex:'sex'},
{header:'名称',dataIndex:'name'},
{header:'描述',dataIndex:'descn'}
]);
var grid = new Ext.grid.GridPanel({
el: 'grid3',
ds: ds,
cm: cm,
sm: sm,//添加的地方
title: 'HelloWorld'
});
3. 如何做Grid上触发事件呢?
下面是一个cellclick事件
grid.addListener('cellclick', cellclick);
function cellclick(grid, rowIndex, columnIndex, e) {
var record = grid.getStore().getAt(rowIndex); //Get the Record
var fieldName = grid.getColumnModel().getDataIndex(columnIndex); //Get field name
var data = record.get(fieldName);
Ext.MessageBox.alert('show','当前选中的数据是'+data);
}
4.如何做Grid中做出快捷菜单效果:
grid.addListener('rowcontextmenu', rightClickFn);//右键菜单代码关键部分
var rightClick = new Ext.menu.Menu({
id:'rightClickCont', //在HTML文件中必须有个rightClickCont的DIV元素
items: [
{
id: 'rMenu1',
handler: rMenu1Fn,//点击后触发的事件
text: '右键菜单1'
},
{
//id: 'rMenu2',
//handler: rMenu2Fn,
text: '右键菜单2'
}
]
});
function rightClickFn(grid,rowindex,e){
e.preventDefault();
rightClick.showAt(e.getXY());
}
function rMenu1Fn(){
Ext.MessageBox.alert('right','rightClick');
}
其Grid如下:
5.如何将一列中的数据根据要求进行改变呢?
比如说性别字段根据其male或female改变显示的颜色,这种ColumnMode中设计:
var cm = new Ext.grid.ColumnModel([
new Ext.grid.RowNumberer(),
sm,
{header:'编号',dataIndex:'id'},
{header:'性别',dataIndex:'sex',renderer:changeSex},
{header:'名称',dataIndex:'name'},
{header:'描述',dataIndex:'descn'}
]);
cm.defaultSortable = true;
function changeSex(value){
if (value == 'male') {
return "<span style='color:red;font-weight:bold;'>红男</span>";
} else {
return "<span style='color:green;font-weight:bold;'>绿女</span>";
}
}
其它两种数据的Grid显示是相同的,其不同之处在于数据获取的过程:
6.Json数据
至于这种数据的类型请大家自己看Ajax的书籍:
//JsonData
var data = {
'coders': [
{ 'id': '1', 'sex': 'male', 'name':'McLaughlin', 'descn': '[email protected]' },
{ 'id': '2', 'sex': 'male','name':'Hunter', 'descn': '[email protected]' },
{ 'id': '3', 'sex': 'female','name':'Harold', 'descn': '[email protected]' },
{ 'id': '4', 'sex': 'male','name':'Harolds', 'descn': '[email protected]' }
],
'musicians': [
{ 'id': '1', 'name': 'Clapton', 'descn': 'guitar' },
{ 'id': '2', 'name': 'Rachmaninoff', 'descn': 'piano' }
]
}
//ds使用的MemoryProxy对象和JsonReader对象
var ds = new Ext.data.Store({
proxy: new Ext.data.MemoryProxy(data),
reader: new Ext.data.JsonReader({root: 'coders'}, [
{name: 'id'},
{name: 'sex'},
{name: 'name'},
{name: 'descn'}
])
});
ds.load();
var grid = new Ext.grid.GridPanel({
el: 'grid3',
ds: ds,
cm: cm,
sm: sm,
title: 'HelloWorld',
autoHeight: true//一定要写,否则显示的数据会少一行
});
grid.render();
7.使用XML数据:
注意,读取XML数据必须在服务器上进行。
XML数据test.xml的内容:
<?xml version="1.0" encoding="UTF-8"?>
<dataset>
<results>2</results>
<item>
<id>1</id>
<sex>male</sex>
<name>Taylor</name>
<descn>Coder</descn>
</item>
</dataset>var ds3 = new Ext.data.Store({
url: 'test.xml', //XML数据
reader: new Ext.data.XmlReader({record: 'item'}, [ //使用XmlReader对象
{name: 'id'},
{name: 'sex'},
{name: 'name'},
{name: 'descn'}
])
});
8.从服务器获取数据和数据翻页控件
从一个服务器文件,如ASP、JSP或Servlet中获得数据二维Array、JSON或XML数据,也可以被Ext读取,并被Grid显示:
服务器文件data.asp:
<%
start = cint(request("start"))
limit = cint(request("limit"))
dim json
json=cstr("{totalProperty:100,root:[")
for i = start to limit + start-1
json =json + cstr("{'id':'") +cstr(i) + cstr("','name':'name") + cstr(i) + cstr("','descn':'descn") + cstr(i) + cstr("'}")
if i <> limit + start - 1 then
json =json + ","
end if
next
json = json +"]}"
response.write(json)
%>
我们可以看到,这个页面会根据传入的start和limit的不同,返回不同的数据,其实质是个分页的代码。下面是start=0,limit=10的JSON数据:
{totalProperty:100,root:[{'id':'0','name':'name0','descn':'descn0'},{'id':'1','name':'name1','descn':'descn1'},{'id':'2','name':'name2','descn':'descn2'},{'id':'3','name':'name3','descn':'descn3'},{'id':'4','name':'name4','descn':'descn4'},{'id':'5','name':'name5','descn':'descn5'},{'id':'6','name':'name6','descn':'descn6'},{'id':'7','name':'name7','descn':'descn7'},{'id':'8','name':'name8','descn':'descn8'},{'id':'9','name':'name9','descn':'descn9'}]}
我们使用分页控件来控制Grid的数据:
Ext.onReady(function(){
var sm = new Ext.grid.CheckboxSelectionModel();
var cm = new Ext.grid.ColumnModel([
new Ext.grid.RowNumberer(),
sm,
{header:'编号',dataIndex:'id'},
{header:'性别',dataIndex:'sex'},
{header:'名称',dataIndex:'name'},
{header:'描述',dataIndex:'descn'}
]);
cm.defaultSortable = true;
var ds = new Ext.data.Store({
proxy: new Ext.data.HttpProxy({url:'data.asp'}),
reader: new Ext.data.JsonReader({
totalProperty: 'totalProperty',
root: 'root'
}, [
{name: 'id'},
{name: 'name'},
{name: 'descn'}
])
});
ds.load({params:{start:0,limit:10}});
var grid = new Ext.grid.GridPanel({
el: 'grid3',
ds: ds,
cm: cm,
sm: sm,
title: 'ASP->JSON',
bbar: new Ext.PagingToolbar({
pageSize: 10,
store: ds,
displayInfo: true,
displayMsg: '显示第 {0} 条到 {1} 条记录,一共 {2} 条',
emptyMsg: "没有记录"
}),
tbar: new Ext.PagingToolbar({
pageSize: 10,
store: ds,
displayInfo: true,
displayMsg: '显示第 {0} 条到 {1} 条记录,一共 {2} 条',
emptyMsg: "没有记录"
})
});
grid.render();
})
10.如何在Grid的上方添加按钮呢?
添加按钮的关键之处在于tbar或bbar属性设置Toolbar工具条:
var grid = new Ext.grid.GridPanel({
el: 'grid3',
ds: ds,
cm: cm,
sm: sm,
title: 'HelloWorld',
tbar: new Ext.Toolbar({
items:[
{
id:'buttonA'
,text:"Button A"
,handler: function(){ alert("You clicked Button A"); }
}
,
new Ext.Toolbar.SplitButton({})
,{
id:'buttonB'
,text:"Button B"
,handler: function(){ alert("You clicked Button B"); }
}
,
'-'
,{
id:'buttonc'
,text:"Button c"
}
]
})
});
11.将GridPanel放入一个Panel或TabPanel中
var tabs = new Ext.TabPanel({
collapsible: true
,renderTo: 'product-exceptions'
,width: 450
,height:400
,activeTab: 0
,items:[
{
title: 'Unmatched'
},{
title: 'Matched'
}
]
});
tabs.doLayout();
var panel = new Ext.Panel({
renderTo: 'panel',
title:'panel',
collapsible:true,
width:450,
height:400,
items:[grid] //管理grid
});
Panel必须有DIV存在。其包含的Component有items管理。
posted @ 2008-11-02 11:05 付厚俊 阅读(116) | 评论 (0) | 编辑 收藏
2008年10月21日
java NIO
摘要: java nio的全称是java new I/O,即一个全新的I/O控制系统,它的API的包名为java.nio,是在jdk1.4后引入的。 nio之所以为为新,在于它并没在原来I/O的基础上进行开发,而是提供了全新的类和接口,除了原来的基本功能之外,它还提供了以下新的特征: ► 多路选择的非封锁式... 阅读全文
posted @ 2008-10-21 17:44 付厚俊 阅读(31) | 评论 (0) | 编辑 收藏
2008年10月20日
轻松实现Apache,Tomcat集群和负载均衡
作者:罗代均 ldj_work#126.com,转载请保持完整性
环境说明
Apache :apache_2.0.55 1 个
Tomcat: apache-tomcat-5.5.17 (zip版) 2个
mod_jk:: mod_jk-apache-2.0.55.so 1个
第一部分:负载均衡
负载均衡,就是apache将客户请求均衡的分给tomcat1,tomcat2....去处理
1.安装apche,tomcat
http://httpd.apache.org/ 下载Apache 2.0.55
http://tomcat.apache.org/download-55.cgi 下载tomcat5.5 zip版本(解压即可,绿色版)
http://apache.justdn.org/tomcat/tomcat-connectors/jk/binaries/win32/jk-1.2.15/ 下载mod_jk,注意和 apache版本匹配
按照jdk,我的路径为:E:/ide/apache/Apache2
解压两份Tomcat, 路径分别为 E:/ide/tomcat1,E:/ide/tomcat2
下载mod_jk
2.修改Apache配置文件http.conf
在apache安装目录下conf目录中找到http.conf
在文件最后加上下面一句话就可以了
include "E:/ide/apache/Apache2/conf/mod_jk.conf"
2. http.conf 同目录下新建mod_jk.conf文件,内容如下
#加载mod_jk Module
LoadModule jk_module modules/mod_jk-apache-2.0.55.so
#指定 workers.properties文件路径
JkWorkersFile conf/workers.properties
#指定那些请求交给tomcat处理,"controller"为在workers.propertise里指定的负载分配控制器
JkMount /*.jsp controller
3.在http.conf同目录下新建 workers.properties文件,内容如下
worker.list = controller,tomcat1,tomcat2 #server 列表
#========tomcat1========
worker.tomcat1.port=8009 #ajp13 端口号,在tomcat下server.xml配置,默认8009
worker.tomcat1.host=localhost #tomcat的主机地址,如不为本机,请填写ip地址
worker.tomcat1.type=ajp13
worker.tomcat1.lbfactor = 1 #server的加权比重,值越高,分得的请求越多
#========tomcat2========
worker.tomcat2.port=9009 #ajp13 端口号,在tomcat下server.xml配置,默认8009
worker.tomcat2.host=localhost #tomcat的主机地址,如不为本机,请填写ip地址
worker.tomcat2.type=ajp13
worker.tomcat2.lbfactor = 1 #server的加权比重,值越高,分得的请求越多
#========controller,负载均衡控制器========
worker.controller.type=lb
worker.controller.balanced_workers=tomcat1,tomcat2 #指定分担请求的tomcat
worker.controller.sticky_session=1
4.修改tomcat配置文件server.xml
如果你在不同电脑上安装tomcat,tomcat的安装数量为一个,可以不必修改tomcat配置文件
我这里是在同一台电脑上安装两个tomcat,所以需要更改其中一个的设置
打开tomcat2/conf/server.xml文件
5.编写一个测试jsp
建立一个目录test.里面新建一个test.jsp,内容为
<%
System.out.println("===========================");
%>
把test放到tomcat1,tomcat2的webapps下
6.启动apache,tomcat1,tomcat2,进行测试
通过 http://localhost/test/test.jsp 访问,查看tomcat1的窗口,可以看到打印了一行"=========="
再刷新一次,tomcat2也打印了一条,再刷新,可以看到请求会被tomcat1,tomcat2轮流处理,实现了负载均衡
第二部分,配置集群
只配置负载均衡还不行,还要session复制,也就是说其中任何一个tomcat的添加的session,是要同步复制到其它tomcat, 集群内的tomcat都有相同的session
1. 修改tomcat1, tomcat2的server.xml,将集群部分配置的在注释符删掉,并将tomcat2的4001端口改为4002,以避免与tomcat冲突,当然,如果是两台电脑,是不用改端口的,去掉注释符即可
2,修改测试项目test
修改test.jsp,内容如下
<%@ page contentType="text/html; charset=GBK" %>
<%@ page import="java.util.*" %>
<html><head><title>Cluster App Test</title></head>
<body>
Server Info:
<%
out.println(request.getLocalAddr() + " : " + request.getLocalPort()+"<br>");%>
<%
out.println("<br> ID " + session.getId()+"<br>");
// 如果有新的 Session 属性设置
String dataName = request.getParameter("dataName");
if (dataName != null && dataName.length() > 0) {
String dataValue = request.getParameter("dataValue");
session.setAttribute(dataName, dataValue);
}
out.print("<b>Session 列表</b>");
Enumeration e = session.getAttributeNames();
while (e.hasMoreElements()) {
String name = (String)e.nextElement();
String value = session.getAttribute(name).toString();
out.println( name + " = " + value+"<br>");
System.out.println( name + " = " + value);
}
%>
<form action="index.jsp" method="POST">
名称:<input type=text size=20 name="dataName">
<br>
值:<input type=text size=20 name="dataValue">
<br>
<input type=submit>
</form>
</body>
</html>
然后在test 新建WEB-INF目录,WEB-INF下新建web.xml,内容如下
<web-app xmlns=" http://java.sun.com/xml/ns/j2ee" xmlns:xsi=" http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4">
<display-name>TomcatDemo</display-name>
<distributable/>
</web-app>
注意:在你的应用的web.xml加入
<distributable/> 即可
ok,讲test复制到tomcat1,tomcat2的webapps下,重启apache,tomcat1,tomcat2,
输入网址 http://localhost/test/test.jsp
新建一个 名称为 xiaoluo ,值为 cdut 的session,提交查询,新开一个ie窗口,再提交查询,如图,可以看到,两个tomcat 是负载均衡,并且session同步的
posted @ 2008-10-20 08:49 付厚俊 阅读(17) | 评论 (0) | 编辑 收藏
2008年10月15日
Ext中FormPanel面板及Form控件横排测试(CSS)
在实际的网页开发中,大部分时间都要涉及到Form表单的处理。在Ext框架中也提供了很多这方面的控件,而且还有一个专门的FormPanel布 局,该布局默认为放在面板上面的所有控件都是换行放置,而在实际应用中为了美观,有些需要横排,特别是Radio控件,这个时候就需要我们重新定制这些控 件的布局了,该例子中使用CSS来实现这些功能,先贴出一张效果图。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Ext中FormPanel面板及Form控件横排测试(CSS)</title>
<link rel="stylesheet" type="text/css" media="all" href="../ext/resources/css/ext-all.css" />
<style type="text/css" media="all">
.allow-float {clear:none!important;} /* 允许该元素浮动 */
.stop-float {clear:both!important;} /* 阻止该元素浮动 */
.sex-male {float:left;}
.sex-female {float:left;padding:0 0 0 20px;}
.age-field {float:left;padding:0 0 0 58px;*padding:0 0 0 50px!important;*padding:0 0 0 50px;}
</style>
</head>
<body>
<script type="text/javascript" src="../ext/adapter/ext/ext-base.js"></script>
<script type="text/javascript" src="../ext/ext-all.js"></script>
<script type="text/javascript" src="../ext/build/locale/ext-lang-zh_CN.js"></script>
<script type="text/javascript">Ext.BLANK_IMAGE_URL = '../ext/resources/images/default/s.gif';</script>
<script type="text/javascript">
Ext.onReady(function() {
//创建Form面板
var fp = new Ext.form.FormPanel({
buttonAlign:'center',
labelAlign:'right',
labelWidth:40,
frame:true,
bodyStyle:'padding:8px 0 0 0;',
items:[{
xtype:'textfield',
fieldLabel:'姓名',
name:'n_username',
id:'i_username',
width:320
},{
xtype:'radio',
fieldLabel:'性别',
boxLabel:'男',
name:'sex',
id:'male',
itemCls:'sex-male', //向左边浮动,处理控件横排
clearCls:'allow-float', //允许两边浮动,在实际生成的HTML结构中有专门的DIV阻断浮动
checked:true
},{
xtype:'radio',
boxLabel:'女',
name:'sex',
id:'female',
itemCls:'sex-female', //向左浮动,处理控件横排
clearCls:'allow-float', //允许两边浮动
hideLabel:true //不显示前面"性别"的标签
},{
xtype:'textfield',
fieldLabel:'年龄',
name:'n_age',
id:'i_age',
itemCls:'age-field', //向左浮动,处理控件横排
width:133
},{
xtype:'textfield',
fieldLabel:'住址',
name:'n_address',
id:'i_address',
itemCls:'stop-float', //不允许浮动,结束控件横排
width:320
}],
buttons:[{
text:'确定',
handler:onOK //实际应用一般是处理fp.getForm.submit()事件
}, {
text:'重置',
handler:function(){ fp.getForm().reset(); }
}],
keys:[{ //处理键盘回车事件
key:Ext.EventObject.ENTER,
fn:onOK,
scope:this
}]
});
//确定按钮事件,这里只是简单获取各控件值,实际应用一般和后台脚本结合
function onOK() {
var strMsg;
strMsg = ‘姓名:’ + fp.getComponent(’i_username’).getValue() + ‘,性别:’;
if (fp.getComponent(’male’).checked) strMsg += ‘男’;
if (fp.getComponent(’female’).checked) strMsg += ‘女’;
strMsg += ‘,年龄:’ + fp.getComponent(’i_age’).getValue();
strMsg += ‘,住址:’ + fp.getComponent(’i_address’).getValue();
alert(strMsg);
}
//创建主窗口
new Ext.Window({
title:’Ext中FormPanel面板及Form控件横排测试(CSS)’,
width:400,
closable:false,
collapsible:true,
draggable:false,
resizable:false,
modal:true,
border:false,
items:[fp],
buttons:[]
}).show();
});
</script>
</body>
</html>
posted @ 2008-10-15 13:03 付厚俊 阅读(37) | 评论 (0) | 编辑 收藏
EXT树重新加载
ext js 创建树
- var Tree = Ext.tree;
-
- deptTree = new Tree.TreePanel({
- el : elementId,
- autoScroll:true,
- animate:true,
- enableDD:true,
- containerScroll: true,
- loader: new Tree.TreeLoader({
- dataUrl: CONTEXT_PATH + "/depts/deptTree.do",
- baseParams: filter
- })
- });
-
-
- root = new Tree.AsyncTreeNode({
- text: rootDesc,
- draggable:false,
- id:'-1'
- });
- deptTree.setRootNode(root);
-
-
- deptTree.render();
- root.expand();
var Tree = Ext.tree;
deptTree = new Tree.TreePanel({
el : elementId,
autoScroll:true,
animate:true,
enableDD:true,
containerScroll: true,
loader: new Tree.TreeLoader({
dataUrl: CONTEXT_PATH + "/depts/deptTree.do",
baseParams: filter
})
});
// set the root node
root = new Tree.AsyncTreeNode({
text: rootDesc,
draggable:false,
id:'-1'
});
deptTree.setRootNode(root);
// render the deptTree
deptTree.render();
root.expand();
ext js 重新加载树
-
-
-
-
-
-
- reload : function (f){
- filter = f;
-
- var loader = deptTree.getLoader();
- deptTree.on('beforeload', function(){
- loader.dataUrl = loader.dataUrl ;
- loader.baseParams = filter;
- });
- root.reload();
- }
posted @ 2008-10-15 12:52 付厚俊 阅读(172) | 评论 (1) | 编辑 收藏
2008年10月10日
ext中树结构带复选框的处理
在Faceye基础版(开源)中,使用了大量的树结构,比如台台管理树,用户RSS订阅及分类树,用户博客分类对,网址导航分类树,开源项目分类树,博客 管理树等大量的树形结构,甚至在用户授权,用户分组,资料分类等也都使用了树结构,树结构的处理,在Faceye中相比比较成熟,这些,是基于前期项目中 对Struts-menu,Dtree等处理
关于Struts-Menu,Dtree的处理,在我早期的博客中都有描述.
带有复选框的树,在实际的项目中,特别是授权结构中,有大量的使用.我们来看一下Faceye中如何处理这种带复选框的树形结构.
首先,Faceye使用了Ext的树解决方案,所以,所有的数据结构,均转化为Ext中树解决方案的数据结构.在这里,我们可以很方便的把这一结构转化为其它树支持的数据结构,比如Strus-menu,dtreee等.
下面是对树完整的脚本处理代码.
[code]
/**
*RoleController
* 角色访问权限管理
* 管理角色对模块的访问权限
* www.faceye.com网络支持系统
* 作者:宋海鹏
[email protected]/
[email protected]
*/
Ext.BLANK_IMAGE_URL = 'scripts/ext/resources/images/vista/s.gif';
com.faceye.compoents.core.security.RoleVisiteModelPermission={
init:function(roleId){
Ext.QuickTips.init();
if(Ext.getCmp('tree')){
Ext.get('tree-viewer').remove(true);
// Ext.getCmp('tree').destroy(this);
Ext.getBody().createChild({tag:'div',id:'tree-viewer'});
}
var tree;
var root;
// if(!tree){
var Tree = Ext.tree;
tree = new Ext.tree.TreePanel(
{
el:'tree-viewer',
id:'tree',
autoScroll:true,
animate:true,
border:false,
enableDD:true,
containerScroll: true,
rootVisible:false,
loader: new Ext.tree.TreeLoader({
dataUrl:'/faceye/treeAction.do?method=treeWithCheckBoxForPermission&roleId='+roleId
// uiProviders:{
// 'col': Ext.ux.CheckColumnNodeUI
// }
})
// columns:[{
//// header:'node',
// dataIndex:'name'
// }]
//
}
);
//Defint node click event,when node is clicked,send data to inner 'div' and show data in
// set the root node
root = new Tree.AsyncTreeNode({
text: 'Common Platform',
draggable:false,
id:'source',
checked:true
// uiProvider: Ext.tree.CheckboxNodeUI
});
tree.setRootNode(root);
/**
*Open node URL in a target contanier
*/
//全部展开
tree.expandAll();
tree.on('checkchange',function(node){
if(!node.isLeaf()){
node.toggle();
}
fireCheckChange(node);
// node.cascade(function(n){
// n.getUI().toggleCheck();
// });
});
/**
* 当fire checkchange时执行
*/
function fireCheckChange(node){
if(node.getUI().isChecked()){
checkedChildrenNodes(node);
checkedParentNodes(node);
}else{
//取得当前节点的所有子节点,包括当前节点
var allChildrenNodes=getAllChildrenNodes(node);
//如果当前节点的所有子节点中,不存在checked=true的节点,那么将当前节点置为checked=false.
//
//如果当前节点有子节点,同时,当前节点checked=false,那么将其所有子节点置为checked=false
for(var i=0;i<allChildrenNodes.length;i++){
if(allChildrenNodes[i].getUI().isChecked()){
allChildrenNodes[i].getUI().toggleCheck();
}
}
unCheckedParentNode(node);
}
}
/**
* 当点击父节点时
* 将其所有子节点选中
*/
function checkedChildrenNodes(node){
//取得本节点的所有子节点,子节点中包括其自己
var allChildrenNodes=getAllChildrenNodes(node);
if(allChildrenNodes.length>1){
for(var i=0;i<allChildrenNodes.length;i++){
if(!allChildrenNodes[i].getUI().isChecked()){
allChildrenNodes[i].getUI().toggleCheck();
}
}
}
}
/**
* 当当前子节点的父节点的所有子节点中
* 不存在checked=true的子节点时,父节点不被选中
*/
function unCheckedParentNode(currentChildNode){
if(currentChildNode.parentNode){
var parentNode=currentChildNode.parentNode;
//取得本父节点下所有被选中的子节点
//包括本父节点本身
var allCheckedChildrenNodes=getCheckedNodes(parentNode);
if(allCheckedChildrenNodes.length === 1){
parentNode.getUI().toggleCheck();
parentNode.attributes.checked=false;
}
if(parentNode.parentNode){
unCheckedParentNode(parentNode);
}
}
}
/**
* 当点击子节点时
* 将父节点选中
*/
function checkedParentNodes(node){
//取得本节点的所有父节点,父节点中包括其自己
var allParentNodes=getAllParentNodes(node);
if(allParentNodes.length>1){
for(var i=0;i<allParentNodes.length;i++){
if(!allParentNodes[i].getUI().isChecked()){
allParentNodes[i].getUI().toggleCheck();
}
}
}
}
/**
* 取得所有子节点中checked 为true的节点ID
* 包括本节点
*/
function getCheckedNodesId(node){
var checked = [];
if( node.getUI().isChecked() || node.attributes.checked ) {
// alert('dfdf'+node.childNodes.length);
checked.push(node.id);
if( !node.isLeaf() ) {
for(var i = 0; i < node.childNodes.length; i++ ) {
checked = checked.concat( getCheckedNodesId(node.childNodes[i]) );
}
}
}
return checked;
};
/**
* 取得所有子节点中checked为true的节点(TreeNode)
* 包括本节点
*/
function getCheckedNodes(node){
var checked = [];
if( node.getUI().isChecked() ) {
checked.push(node);
if( !node.isLeaf() ) {
for(var i = 0; i < node.childNodes.length; i++ ) {
checked = checked.concat( getCheckedNodes(node.childNodes[i]) );
}
}
}
return checked;
};
/**
* 取得一个节点的所有子节点
* 包括本节点
*/
function getAllChildrenNodes(node){
var children = [];
children.push(node);
if(!node.isLeaf()){
for(var i=0;i<node.childNodes.length;i++){
children = children.concat(getAllChildrenNodes(node.childNodes[i]));
}
}
return children;
};
/**
* 取得一个节点的所有父节点
*
*/
function getAllParentNodes(node){
var parentNodes=[];
parentNodes.push(node);
if(node.parentNode){
parentNodes = parentNodes.concat(getAllParentNodes(node.parentNode));
}
return parentNodes;
};
/**
* 取得所有checked=true的节点ID
*/
function getAllChecked(){
return getCheckedNodesId(root);
}
tree.on('click', function (node){
if(node.isLeaf()){
// Ext.get('content-iframe').dom.src = node.attributes.link+'&node='+node.id;
//define grid;
return true;
}else{
/**
*open node by single click,not double click.
*/
node.toggle();
}
});
tree.on('dblclick',function(node){
if(node.isLeaf()){
return true;
}else{
node.toggle();
fireCheckChange(node);
// this.fireEvent('checkchange', this.node, true);
}
});
// render the tree
// }
tree.render();
root.expand();
var win=new Ext.Window({
layout:'fit',
//模式窗口
modal:true,
width:450,
height:300,
closeAction:'hide',
plain: true,
// margins:'5 5 5 5',
title:'为角色授权',
buttonAlign:'center',
buttons: [{
text:'确定',
scope:com.faceye.compoents.core.security.SelectRoles,
type:'submit',
disabled:false,
handler:function(){
var checked=getAllChecked().join(',');
Ext.Ajax.request({
url:'/faceye/roleAction.do?method=permission',
failure:function(){
Ext.Msg.alert('角色授权','角色授权失败!');
win.hide();
},
success:function(){
Ext.Msg.alert('角色授权','角色授权成功!');
win.hide();
},
params:{
treeIds:checked,
roleId:roleId
}
});
}
},{
text: '放弃',
handler: function(){
// formItemSelector.getForm().reset();
if(win.getComponent('tree')){
// Ext.get('tree-viewer').remove();
// win.destroy();
// win.remove(tree);
}
win.hide();
// win.disable();
// tree.disable();
}
}
]
});
win.add(tree);
win.show();
}
};
[/code]
在带有复选框的树形结构处理中,主要需要解决这么几个问题:
1.我们的树,是无限级别的
2.当选中父节点时,其所有子节点,包括直接子节点和间接子节点,一并选中
3.当一个节点的所有子节点被取消选中状态时,其父节点要处于未被选中的状态.
因为树向来是子子孙孙无穷尽的一个东西,所以在这里,需要用到比较多的递归处理,不管是在脚本方面,还是在后台代码里,都需要用到一些递归处理,比如我们所提供的方法中:
[code] /** * 取得一个节点的所有父节点 * */ function getAllParentNodes(node){ var parentNodes=[]; parentNodes.push(node); if(node.parentNode){ parentNodes = parentNodes.concat(getAllParentNodes(node.parentNode)); } return parentNodes; }; [/code]
在这里,提供了这样一样功能,既:取得当前点击(选中)节点的所有父节点,这些父节点中,包括直接父节点和间接父节点,也就是说,在这里,会取得当前选中节点的父亲节点,父亲节点的父亲节点....一直到根节点为止.
类似的方法有:
[code] /** * 取得一个节点的所有子节点 * 包括本节点 */ function getAllChildrenNodes(node){ var children = []; children.push(node); if(!node.isLeaf()){ for(var i=0;i<node.childNodes.length;i++){ children = children.concat(getAllChildrenNodes(node.childNodes[i])); } } return children; }; [/code]
这是取得一个节点所有子节点的方法.处理跟上面类似.
通过以上这些处理,我们就可以很方便的处理带复选框的树形结构.
而在这里,我们最终真正关心的,是那些被我们选中的节点,于是,我们需要取得所有被选中的节点,在这里我们需要使用另外一个方法:
[code]
/**
* 取得所有子节点中checked为true的节点(TreeNode)
* 包括本节点
*/
function getCheckedNodes(node){
var checked = [];
if( node.getUI().isChecked() ) {
checked.push(node);
if( !node.isLeaf() ) {
for(var i = 0; i < node.childNodes.length; i++ ) {
checked = checked.concat( getCheckedNodes(node.childNodes[i]) );
}
}
}
return checked;
};
[/code]
在这里,我们同样使用到了递归.
同时,我们可以看到,在生成树的时候,我们使用了这样一个loader:
[code]
loader: new Ext.tree.TreeLoader({
dataUrl:'/faceye/treeAction.do?method=treeWithCheckBoxForPermission&roleId='+roleId
// uiProviders:{
// 'col': Ext.ux.CheckColumnNodeUI
// }
})
[/code]
这是我们从后台取数据的方法,同时,要在前台加入check column
要加入check column,我们需要看以下代码:
在方法:treeWithCheckBoxForPermission中,我们做了以下操作:
[code]
/**
* 构造带有checkbox的tree,为角色进行节点授权做准备
* @param mapping
* @param form
* @param request
* @param response
* @return
*/
public ActionForward treeWithCheckBoxForPermission(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response) {
String json = "";
// JSONArray ja=new JSONArray();
String roleId=this.getHttp().getParameter(request, "roleId");
if (super.getHttp().getParameter(request, "node").equals("source")) {
json = this.getTreeService().treeJSONWithCheckBox(roleId);
} else {
String currentNode = super.getHttp().getParameter(request, "node");
json = this.getTreeService().treeJSONWithCheckBox(
this.getTreeService().getTransedTrees(),
currentNode,roleId);
}
super.jsonPrint(response, json);
// return mapping.findForward("system.admin.face");
return null;
}
[/code]
在这里,我们主要对原始的树形结构进行了处理,在每个节点上面,打上了checkbox标识:
[code] public String treeJSONWithCheckBox(Serializable roleId) { // TODO Auto-generated method stub try { List trees = this.getTransedTrees(); String result = this.treeJSONWithCheckBox(trees, roleId); return result; } catch (Exception e) { log.info(">>>>faceye error in method:treeJSON() is" + e.toString()); return null; } } [/code]
这里最主要的一个方法是:
[code] public String treeJSONWithCheckBox(List source, Serializable roleId) { // TODO Auto-generated method stub if (source == null || source.isEmpty()) { return null; } List roots = this.getRoots(source); Iterator it = roots.iterator(); StringBuffer json = new StringBuffer(); json.append("["); while (it.hasNext()) { Map item = (Map) it.next(); json.append(this.transTree2JSONWithCheckBox(source, item, roleId)); json.append(","); } json.deleteCharAt(json.lastIndexOf(",")); json.append("]"); return json.toString(); } [/code]
在这里,我们调用了最终方法:
[code]
transTree2JSONWithCheckBox
[/code]
对树结构进行最终处理
[code]
private String transTree2JSONWithCheckBox(List source, Map tree,
Serializable roleId) {
StringBuffer json = new StringBuffer();
json.append("{");
json.append("/"text/":");
json.append("/"");
json.append(tree.get(StringPool.TREE_NAME).toString());
json.append("/"");
json.append(",");
json.append("/"id/":");
json.append("/"");
json.append(tree.get(StringPool.TREE_ID).toString());
json.append("/"");
json.append(",");
json.append("/"leaf/":");
if (this
.isHaveChildren(source, tree.get(StringPool.TREE_ID).toString())) {
json.append("false");
} else {
json.append("true");
}
json.append(",");
json.append("/"cls/":");
json.append("/"file/"");
if (tree.containsKey(StringPool.TREE_ACTION)
&& null != tree.get(StringPool.TREE_ACTION)) {
json.append(",");
json.append("/"link/":");
json.append("/"");
json.append(tree.get(StringPool.TREE_ACTION).toString());
json.append("/"");
} else {
json.append(",");
json.append("/"link/":");
json.append("/"");
json.append("#");
json.append("/"");
}
json.append(",");
json.append("/"checked/":");
// json.append("true");
json.append(this.isNodeChecked(tree.get(StringPool.TREE_ID).toString(),
roleId));
json.append("}");
return json.toString();
}
[/code]
可以看到,我们在这里,加入了:[code]checked:true[/code]这样的属性.
至此,可以看到完整的带有复选框的树形结构的处理流程,完整代码,请从 http://code.google.com/p/faceye下载.
posted @ 2008-10-10 21:43 付厚俊 阅读(168) | 评论 (0) | 编辑 收藏
2008年10月8日
Java下的框架编程之cglib的应用
Java下的框架编程之cglib的应用 |
|
|
Proxy 可以看作是微型的AOP,明白提供了在继承和委托之外的第三个代码封装途径,只要有足够的想象力,可以做得非常好玩,Spring的源码里用Proxy就 用得很随便,看得我非常眼红。可惜Proxy必须基于接口。因此Spring的做法,基于接口的用proxy,否则就用cglib。AOP么,一般小事非 compoent一级的就不麻烦AspectJ出手了。 cglib的Enhancer说起来神奇,用起来一页纸不到就讲完了。 它的原理就是用Enhancer生成一个原有类的子类,并且设置好callback到proxy, 则原有类的每个方法调用都会转为调用实现了MethodInterceptor接口的proxy的intercept() 函数:
public Object intercept(Object o,Method method,Object[] args,MethodProxy proxy) |
在intercept()函数里,你可以在执行Object result=proxy.invokeSuper(o,args);来执行原有函数,在执行前后加入自己的东西,改变它的参数值,也可以瞒天过海,完全 干别的。说白了,就是AOP中的around advice。 AOP没有出现以前,该领域经典的设计模式是Decorator,像Java IO Stream的设计就是如此。不过,如果为每个DAO, 每个方法的写Decorator函数会写死人的,所以用上cglib的好处是一次过拦截所有方法。 另外,cglib除了Enhancer之外,还有BulkBean和Transform,都是Hibernate持久化的基础,但文档贫乏,一时还没去看怎么用。 1.AOP里讲了一百遍啊一百遍的log aspect在cglib是这样做的:
public class LogDAOProxy implements MethodInterceptor
{
private Logger log=Logger.getLogger(LogDAOProxy.class);
private Enhancer enhancer=new Enhancer();
//返回DAO的子类
public Object getDAO(Class clz)
{
enhancer.setSuperclass(clz);
enhancer.setCallback(this);
return enhancer.create();
}
//默认的拦截方法
public Object intercept(Object o,Method method,Object[] args, MethodProxy proxy) throws Throwable
{
log.info("调用日志方法"+method.getName());
Object result=proxy.invokeSuper(o,args);
return result;
}
} |
应用的代码:
LogDAOProxy proxy = new LogDAOProxy();
GoodsDAO dao = (GoodsDAO)proxy.getDAO(GoodsDAO.class);
dao.insert(goods); |
2.而在Spring的管理下应该略加修改的高级Decorator 上面的例子用return enhancer.create();创建子类实例,但在Spring管理下,一些Bean的实例必须由Spring来创建和管理,而不由enhancer来创建的。所以我对上述用法略加修改,使它真正当一个Proxy的角色,请对比黑体字的部分。
public class LogDAOProxy implements MethodInterceptor
{
private Logger log=Logger.getLogger(LogDAOProxy.class);
private Object dao=null;
private Enhancer enhancer=new Enhancer();
//返回DAO的子类
public Object getDAO(Class clz,Object dao)
{
this.dao = dao;
enhancer.setSuperclass(clz);
enhancer.setCallback(this);
return enhancer.create();
}
//默认的拦截方法
public Object intercept(Object o,Method method,Object[] args, MethodProxy proxy) throws Throwable
{
log.info("调用日志方法"+method.getName());
Object result=proxy.invoke(dao, args);
return result;
}
} |
可见,原来模式里在getDao()时由enhancer创建dao,而 调用intercept时则将enhancer创建的dao以Object o参数传回。 而新模式里,dao在getDao()时从外面传入,enhancer.create()返回的是一个proxy. 而调用intercept时,实际会用之前传入的dao进行操作,而忽略Object o参数传入的proxy。 有点遗憾, intercept函数里MethodProxy的Signature是固定的,即客户如果调用foo(String),你不可以用proxy.invoke偷换成foo(String,String); |
posted @ 2008-10-08 10:38 付厚俊 阅读(23) | 评论 (0) | 编辑 收藏
2008年10月7日
使用Eclipse+spket开发Spket的安装
1、Plugin:
最低要求: eclipse平台运行时3.2.x ,
文件为: spket-1.6.4.1.zip(与别的插件的安装无异,相信不用我多说了吧
2、Spket IDE:
最低要求:你需要java 1.5或更高版本运行安装程序,可独立使用(不用装Eclipse
文件为:spket-1.6.4.1.jar
安装步骤:1、打开cmd,2、在spket-1.6.4.1.jar文件目录下输入java -jar spket-1.6.4.1.jar2、Spket IDE:
最低要求:你需要java 1.5或更高版本运行安装程序,可独立使用(不用装Eclipse
文件为:spket-1.6.4.1.jar
安装步骤:1、打开cmd,2、在spket-1.6.4.1.jar文件目录下输入java -jar spket-1.6.4.1.jar
二、使用
安装完后你去试,发现没有用,其实还有一些操作如下:
1、执行这个操作:Window → Preferences → Spket → JavaScript Profiles → New
2、在弹出框中输入ExtJs,然后点OK。
3、然后选择ExtJs点Add Library,把你下载的ext包中找到/source/ext.jsb加载进来
4、然后先中ExtJs点击右边的default按钮.
5、然后点击确定就搞定。你现在有eclipse中建立一个JS用Ext.就会出现提示咯!!!
posted @ 2008-10-07 12:11 付厚俊 阅读(266) | 评论 (0) | 编辑 收藏
Ext Form布局
现在我们要实现的效果是: 点击确定,把值传到另一页面!,如下:
原页面js代码为:
Ext.onReady(function(){
Ext.QuickTips.init();
var form=new Ext.FormPanel({
frame:true,
width:300,
//monitorValid:true,//绑定验证
layout:"form",
labelWidth:70,
title:"添加个人信息",
labelAlign:"left",
renderTo:Ext.getBody(),
submit: function(){
this.getEl().dom.action =
'GetForm.aspx',
this.getEl().dom.method='POST',
this.getEl().dom.submit();
},
items:[{
xtype:"textfield",
fieldLabel:"用户名",
//id:"UserName",
allowBlank:false,
blankText:"不能为空,请正确填写",
name:"UserName",
anchor:"90%"
},{
xtype:"textfield",
fieldLabel:"昵称",
//id:"SmallName",
name:"SmallName",
anchor:"90%"
},{
xtype:"datefield",
fieldLabel:"注册日期",
//id:"RegDate",
name:"RegDate",
anchor:"90%"
}],
});
接受页面GetForm.aspx的cs代码为:
protected
void Page_Load(object sender, EventArgs e)
{
string UserName = Request.Form["UserName"];
string SmallName = Request.Form["SmallName"];
string RegDate = Request.Form["RegDate"];
Response.Write(UserName +
"<br/>"
+ SmallName +
"<br/>"
+ RegDate);
}
因为很简单,我做个简要说明: //几点说明
1.首先定义submit参数的执行函数,即:
submit: function(){
this.getEl().dom.action =
'GetForm.aspx',//转向页面地址
this.getEl().dom.method='POST',//方式
this.getEl().dom.submit();//提交!
},
2.为按钮添加触发相应的提交(取消)事件(这样就不是默认的ajax提交):
buttons:[{text:"确定",handler:login,formBind:true},{text:"取消",handler:reset}]
});
function login(){
form.form.submit();//提交
}
function reset(){
form.form.reset();//取消
}
3.如果你想绑定验证,在form表单添加参数monitorValid:true,然后在按钮配置参数中添加formBind:true,如
buttons:[{text:"确定",handler:login,formBind:true},{text:"取消",handler:reset}]
则只有所有的填写字段都满足条件时,"确定"方可提交!如下图,
好了,一个简单的formpanel的提交的原理弄清楚啦!
有关form提交数据的方法有多种,大家可以参考http://www.17ext.com/showtopic-55.aspx(三种ext提交数据的方式),
以后有机会我们再讨论!
下面我们来做个复杂点(只是样子)的form,示例一下(还是上面的原理)!
效果图:
传到GetForm.aspx页面后显示:
不过在传过来的页面要记得ValidateRequest="false",安全编码我就暂且不讨论了!
js代码:
Ext.onReady(function(){
Ext.QuickTips.init();
var form=new Ext.FormPanel({
frame:true,
width:500,
monitorValid:true,//把有formBind:true的按钮和验证绑定
layout:"form",
labelWidth:55,
title:"添加个人信息",
labelAlign:"right",
renderTo:Ext.getBody(),
submit: function(){
this.getEl().dom.action =
'GetForm.aspx',
this.getEl().dom.method='POST',
this.getEl().dom.submit();
},
items:[{
xtype:"panel",
layout:"column",
fieldLabel:"用户名",
isFormField:true,
items:[{
columnWidth:.5,
xtype:"textfield",
allowBlank:false,
blankText:"不能为空,请填写",
name:"UserName",
anchor:"90%"
},{
columnWidth:.20,
layout:"form",
labelWidth:40,
labelAlign:"right",
items:[{
xtype:"radio",
fieldLabel:"性别",
boxLabel:"男",
name:"Sex",
checked:true,
inputValue:"man",//这里如果用value,值是on,所以用inputValue(出现这种情况的是radio,checkbox)
anchor:"95%"
}]
},{
columnWidth:.30,
layout:"form",
labelWidth:1,//让标签宽度为很小的值(奇怪的是为0时反而不行)
items:[{
xtype:"radio",
boxLabel:"女",
labelSeparator:"",//去除分隔符“:”
name:"Sex",
inputValue:"woman",
anchor:"95%"
}]
}]
},{//上面是第一行
xtype:"panel",
layout:"column",
fieldLabel:"出生日期",
isFormField:true,
items:[{
columnWidth:.5,
xtype:"datefield",
name:"BirthDate",
anchor:"90%"
},{
columnWidth:.5,
layout:"form",
labelWidth:40,//注意,这个参数在这里可以调整简单fieldLabel的布局位置
items:[{
xtype:"combo",
name:"Degree",
fieldLabel:"学位",
store:["小学","初中","高中","专科","本科","硕士","博士"],
emptyText:"请选择适合你的学历
",
anchor:"90%"
}]
}]
},{//上面是第二行
xtype:"panel",
layout:"column",
isFormField:true,
fieldLabel:"使用框架",
items:[{
columnWidth:.2,
xtype:"checkbox",
boxLabel:"Spring.net",
name:"SpringNet",
inputValue:"spring"//这里如果用value,值是on,所以用inputValue
},{
columnWidth:.2,
layout:"form",
labelWidth:1,
items:[{
xtype:"checkbox",
boxLabel:"Nhibernate",
labelSeparator:"",
name:"NHibernate",
inputValue:"nhibernate",
anchor:"95%"
}]
},{
columnWidth:.6,
layout:"form",
labelWidth:1,
items:[{
xtype:"checkbox",
boxLabel:"Linq",
labelSeparator:"",
name:"Linq",
inputValue:"linq",
anchor:"95%"
}]
}]
},{//上面是第三行
xtype:"textfield",
fieldLabel:"Email",
name:"Email",
vtype:"email",//email验证,如果想自定义验证的话,请参见前面的文章
vtypeText:"email格式错误!",
anchor:"56%"//控制文本框的长度
},{//上面是第四行
xtype:"textarea",
fieldLabel:"个性签名",
name:"OneWord",
height:50,
anchor:"95%"
},{//上面是第五行
xtype:"htmleditor",
fieldLabel:"想说的话",
name:"WantToSay",
anchor:"95%",
enableAlignments:false,//去除左右对齐工具栏
enableLists:false//去除列表工具栏
}],
buttons:[{text:"确定",handler:login,formBind:true},{text:"取消",handler:reset}]
});
function login(){
form.form.submit();
}
function reset(){
form.form.reset();
}
});