本文于去年年底完成,由于篇幅原因,裁减后提交到《程序员》杂志社,并于2007年2月出版在《程序员》杂志上,文章改名为《开源RIA架构方案-Openlaszlo与db4o》。时隔半年,我把完整的文章提交上来,希望对想要了解 Openlaszlo 的开发者提供帮助。另外,发表在本 blog 上的这篇文章版权归《程序员》杂志社所有。
前言
RIA
(
Rich Internet Applications
),这个术语已经出现多年,随着开源社区和厂商的不断贡献,涌现出许多
RIA
产品,几个重要产品分别是:
Adobe Flex
——
基于
Flash
、
actionscript
、
MXML
。
OpenLaszlo
——
基于
Flash
、
JavaScript
、
LZX
。
Dojo
——
基于
JavaScript
、
DHTML
、
XML
。
XUL
——
基于
XUL
、
JavaScript
。
除了以上产品外,还有大量的
Ajax
、
Applet
开源项目。他们的目的只有一个:简化并改善
Web
应用程序的交互操作,使应用程序可以提供更丰富、更具有交互性和响应性的用户体验。
本文
UI
方面,我们关注重点是
OpenLaszlo
。
OpenLaszlo
的前身是
LPS
(
Laszlo Presentation Server
),由
Laszlo Systems
公司在
2002
年发布,
LPS
是那个时代诞生的第一个
RIA
产品。根据市场和用户的需要,
Laszlo Systems
公司于
2004
年在
CPL
协议下发布了
LPS
的开源产品
OpenLaszlo
。目前,代号为“
Legals
”的
OpenLaszlo
即将在明年第一季度发布,它的出现使
OpenLaszlo
能够支持
Ajax
(
DHTML
)。
“
Legals
”的下一个项目代号为“
Osprey
”,它的目标是支持
swf9
的运行目标,也就是当前
Flex 2
所采用的运行文件版本,预计发布时间为明年的第二或者第三季度。
代号为
“
Orbit
”
的项目是
Sun
和
Laszlo Systems
公司共同开发的产品
,
目的是能让
OpenLaszlo
程序运行在包括
Java ME
在内的任何
Java
平台上
,
比如移动电话、
PDA
、电视机顶盒、打印机
,
等等。
OpenLaszlo
优势在于它是开源的
(
遵循
CPL
协议
),
基于开发者熟悉的技术
(
JavaScript
、
XML
)
。在
SOLO
(
Standalone OpenLaszlo Output
)方式下可部署到任意的
HTTP Web
服务器,在非
SOLO
方式(
Proxied
方式)下可部署在装有
OpenLaszlo Server
的
Linux
、
UNIX
、
Windows
、
Mac OS X
操作系统上,并支持任意的
Java Servlet
容器、
Java EE
应用服务器。只要您的浏览器支持
Flash 6
或者更高版本,那么就可以体验
OpenLaszlo
了。
本文利用
db4o
作为数据库,
db4o
是一个开源的纯面向对象数据库引擎,对于
Java
与
.NET
开发者来说都是一个简单易用的对象持久化工具。同时,
db4o
已经被众多的企业级应用验证为具有优秀性能的面向对象数据库。可以通过《
开源面向对象数据库 db4o 之旅: 初识 db4o
》、《
开源面向对象数据库 db4o 之旅: db4o 查询方式
》系列文章来了解。
OpenLaszlo
安装
以
Windows 2000/XP
安装为例,首先安装
Java 1.4
或以上版本,接着下载
OpenLaszlo Server 3.3.3
,这个过程并不困难,安装完毕会自动进入
OpenLaszlo
欢迎页面,很漂亮的世界时钟。
现在就可以立即体验 OpenLaszlo 的魅力了,只要您有任何一款 XML 编辑器就行,哪怕是 Windows 自带的记事本。转到 OpenLaszlo 的默认安装目录“C:\Program Files\OpenLaszlo Server 3.3.3\Server\lps-3.3.3”,新建文件夹“hello”,然后在“hello”文件夹下新建“hello.lzx”文件,向其中写入:
<
canvas
width
="200"
>
<
text
>
Hello World!
</
text
>
</
canvas
>
第一个程序完成
,
在
IE
浏览器输入如下
地址
,
如图一所示。

图一
第一个
laszlo
页面
“效果很不错
……
”,也许您还在欣赏自己地杰作,不过马上感到这样开发不太方便,好在
OpenLaszlo
为我们提供了
Eclipse
插件
IDE4Laszlo
,
IDE4Laszlo
以前是
IBM alphaWorks
项目,现在已经开源给
Eclipse
,可在
这里
找到,目前的版本是
0.2.0
。安装插件之前还必须确保您的
Eclipse
安装了
WTP1.0 SDK
。
接下来安装插件,在
Eclipse
的菜单栏选择“
Select Help >> Software Updates >> Find and Install
”再选中“
Search for new features to install
”,点击“
New Archived Site
”按钮选择刚才下载的插件
zip
文件,最后根据提示即可顺利完成插件安装。
上面的
OpenLaszlo Server
依靠
Tomcat
服务器为运行环境,不过
OpenLaszlo
也可以运行在例如
Jboss
、
WebLogic
、
WebSphere
等服务器中,并构建自己的运行环境。本应用程序是在
OpenLaszlo
自己的运行环境中构建,可以在
这里
下载。
程序架构
与传统 Web 应用一样,RIA 应用也遵循 MVC 模式,只不过客户端要依赖控制端和服务端提供数据来更新视图,所以多了一个数据层。本实例为了简化程序,使用 JSP 文件来作为控制层与数据层的桥梁,当用户进行操作请求时,OpenLaszlo 向 JSP 发出请求,JSP 获得请求并分析应该调用哪种业务操作,业务层根据所请求的方法与持久层进行交互,将获取的数据以 XML 字符串的形式返回,并通过 JSP 页面传递给 OpenLaszlo 应用。图二展示了本实例的程序架构与数据流转过程。

图二 OLJD架构图
下面在 Eclipse 建立一个 Laszlo 工程以展示这个应用的程序结构(由于该应用程序是基于独立的 OpenLaszlo 运行环境,所以未使用 IDE4Laszlo 提供的特性)。
打开 Eclipse,创建一个名为“openlaszloWITHdb4o”的 Web 应用程序。接着转入该程序的所在目录,为了能保证该应用程序能顺利加入 RIA 特性,请结合图三进行如下操作:
1、
从 OpenLaszlo 独立运行环境的 war 包中拷贝“openlaszlo-3.3.3-servlet\lps”目录到“openlaszloWITHdb4o\”下,该目录是 OpenLaszlo 的组件库。
2、
复制“openlaszlo-3.3.3-servlet\WEB-INF\lib”目录到“openlaszloWITHdb4o\WEB-INF\”,该目录是 OpenLaszlo 的支持库,然后在该项目的“Java Build Path”中引入这些库,同时再引入 dom4j 和 db4o 的支持库,应用程序的服务器端会用到。
3、
复制“openlaszlo-3.3.3-servlet\WEB-INF\lps”目录到“openlaszloWITHdb4o\WEB-INF\”,该目录是 OpenLaszlo 的组件配置信息。
4、
在“openlaszloWITHdb4o\src”下,分别创建 bo、com 包和类文件,接着创建“helloDb4oUser.lzx”和“peopleoperat.jsp”文件。
5、
最后,把 war 包的 web.xml 文件拷贝过来,启动 OpenLaszlo Server 引擎全靠它了。

图三 eclipse中的工程文件结构
服务器端代码
应用程序使用了 Java 5 特性,请保证您的开发环境与本文一致。bo 包是整个用户管理应用程序的数据模型,People 类定义了“用户”,包含姓名、电话等基本信息,请
下载程序源码查看
。
com
包是与持久层交互的重要部分,Db4oInit 类是一个 Servlet,它的作用是随 Servlet 容器同时启动 db4o 数据库,并作为 Server 运行,以便在运行时通过 db4o 对象管理工具进行管理,了解 db4o 的 Server/
Client
模式的具体含义,请参考
《开源面向对象数据库 db4o 之旅: db4o 查询方式》
。最后,不要忘了在 web.xml 中加载 Db4oInit Servlet。
Db4oInit
的
init
方法,打开一个名为“auto.yap”的 db4o 数据库文件,不用担心,如果是因为第一次运行而没有该文件,db4o 会自动创建,除了数据库文件,还要加上端口号,在这里我们使用 1010,最后进行口令授权。
package
com;
import
javax.servlet.ServletException;
import
javax.servlet.http.HttpServlet;
import
com.db4o.Db4o;
import
com.db4o.ObjectServer;
public
class
Db4oInit
extends
HttpServlet {
private
ObjectServer server;
public
void
destroy() {
server.close();
super
.destroy();
}
public
void
init()
throws
ServletException {
this
.server
=
Db4o.openServer(
"
auto.yap
"
,
1010
);
this
.server.grantAccess(
"
admin
"
,
"
123456
"
);
}
}
ContactService
类是 db4o 的 Client 端,用于响应业务操作,包含了最基本的 CRUD 操作。值得注意的是
getPeopleXml
方法,我们利用 db4o 的 QBE 方式查询数据库,如果您愿意,还可以使用 SODA、NQ 方式查询数据库。之后利用 dom4j 进行对象的序列化,在此笔者还推荐 XStream 结合 java 5 的 annotation 进行序列化。新增、删除、修改很简单,不再敷述,请下载程序源码查看,注意每次操作完之后 commit 事务即可。
package
com;
import
org.dom4j.Document;
import
org.dom4j.DocumentHelper;
import
org.dom4j.Element;
import
com.db4o.Db4o;
import
com.db4o.ObjectContainer;
import
com.db4o.ObjectSet;
import
bo.People;
import
java.io.IOException;
public
class
ContactService {
private
static
final
String HOST
=
"
localhost
"
;
//
server host name
private
static
final
int
PORT
=
1010
;
//
server port
private
static
final
String USER
=
"
admin
"
;
//
server username
private
static
final
String PASSWORD
=
"
123456
"
;
//
server password
/**
* retrieve data from db4o as XML String
*
@return
XML String
*/
public
String getPeopleXml(){
ObjectContainer db
=
null
;
try
{
db
=
Db4o.openClient(HOST,PORT,USER,PASSWORD);
People peo
=
new
People();
ObjectSet
<
People
>
objectSet
=
db.get(peo);
Document peplDoc
=
DocumentHelper.createDocument();
peplDoc.setXMLEncoding(
"
UTF-8
"
);
Element rootNode
=
peplDoc.addElement(
"
root
"
);
for
(
int
i
=
0
;i
<
objectSet.size();i
++
){
Element peplNode
=
rootNode.addElement(
"
node
"
);
People eachPeople
=
(People)objectSet.get(i);
peplNode.addAttribute(
"
userId
"
,String.valueOf(eachPeople.getUserId()));
peplNode.addAttribute(
"
userName
"
,eachPeople.getUserName());
peplNode.addAttribute(
"
userPhone
"
,eachPeople.getUserPhone());
peplNode.addAttribute(
"
userEmail
"
,eachPeople.getUserEmail());
peplNode.addAttribute(
"
userAddress
"
,eachPeople.getUserAddress());
}
return
peplDoc.getRootElement().asXML();
}
catch
(IOException e){
e.printStackTrace();
return
"
<error/>
"
;
}
finally
{
if
(db
!=
null
){
db.close();
}
}
}
}
peopleoperat.jsp
代码也相对简单,请
下载程序源码查看
。
页面代码
要编写 OpenLaszlo 服务器
能运行的代码,就必须使用 LZX 语言,LZX
是一种面向对象的基于标记的语言
,
利用
XML
和
JavaScript
语法构建
RIA
表现层
,
通常这些编写完成的代码会经过 OpenLaszlo 编译器进行编译,最后可作为独立的 swf 文件或部署在服务器上运行。
LZX
语言使用开发者熟悉的语法和命名方式,因此能较容易地融入现有编程环境。
LZX
语言具有继承、封装、多态等面向对象语言所具有的特性。通常,一个标签就等于实例化了一个类,比如 <view> 就是 LzView 的实例。值得注意的是,和基于 DHTML/JavaScript 的程序有着概念上的不同,典型的 DHTML/JavaScript 程序是直接由浏览器解析、执行嵌入其中的 JavaScript;而 LZX 中的 JavaScript 则是在 OpenLaszlo 服务器端编译成客户端 Flash 解析引擎能运行的字节码,再下载到浏览器执行的(swf
文件
)。
LZX
程序是运行在名为 canvas 的可视对象基础上的,也就是以 <canvas> 开始并以 </canvas> 结束的标签中。在 canvas 中,有许多 view,这些 view 嵌套着业务逻辑、可视化特性、可编程属性、尺寸、位置、背景颜色、不透明性、可点击性、伸缩性,等等信息。view 也可以包含资源,例如图像或视频,也可以动态绑定任意 XML 格式数据集。
现在我们就进入代码部分,首先观察前三行:
<
canvas
debug
="true"
height
="580"
width
="780"
title
="Hello db4o, openlaszlo"
fontsize
="12"
>
<
debug
x
="0"
y
="400"
width
="300"
height
="200"
/>
<
splash
/>
第一行代码向我们表明了 LZX 语言的面向对象特性,canvas 对象的 debug 属性为了让程序调试时能在 debugger 窗口打印出调试信息,height、width 属性分别指定了 canvas 对象的高和宽,超过这一大小,将不会被显示,title 属性和 HTML 中 title 标签功能一样,fontsize 属性作为一个全局参数,如果没有明确指定,那么在 canvas 中所有的 view 都将使用该字体大小。第二行代码实例化 Debug 对象,并规定了 debugger 的高和宽,除此以外,还定义了窗口出现的 x(横坐标)、y(纵坐标),这些都是相对坐标。第三行定义了 splash 对象,该对象是加载应用程序时的进度条。
LZX
还提供了强大的绘图功能,下面就让它小试牛刀吧:
<
drawview
name
="banner"
width
="780"
height
="50"
>
<
handler
name
="oninit"
>
this.beginPath();
this.lineTo(780, 0);
this.lineTo(780, 50);
this.lineTo(0, 50);
this.lineTo(0, 0);
this.closePath();
var g = this.createLinearGradient(0,0,0,50);
this.globalAlpha = 0;
g.addColorStop(0, 0x666666);
this.globalAlpha = 1;
g.addColorStop(1, 0xFFFF00);
this.fillStyle = g;
this.fill();
</
handler
>
</
drawview
>
在本应用中,我们要实现的是一个渐变的黄色 banner。drawview 类是专门绘图的,handler 类捕获了 oninit 内置事件。我们这样设想:手中有只画笔,从某点开始,然后按照一定路径进行边框描绘,最后填充颜色。同理,this.beginPath() 设置画笔的开始点(0,0 坐标),然后绘制直线到 780,0 坐标,接着再绘制到 780, 50 坐标,然后闭合方框(this 表示 drawview 自身对象实例),this.createLinearGradient(0,0,0,50) 方法是在按照 x0, y0, x1, y1 这种路径进行梯度过渡,this.globalAlpha、g.addColorStop() 是一系列的颜色填充设置,从 0x666666 颜色均匀过渡到 0xFFFF00 颜色,最后 this.fillStyle = g 应用该设置,this.fill() 填充即可。
有了以上基础,我们开始解析可视化组件,下面是构造边框为绿色的 bordergreenbox 类:
<
class
name
="bordergreenbox"
extends
="view"
x
="1"
y
="145"
width
="${canvas.width-2}"
bgcolor
="0x9ae900"
>
<
view
name
="innerbox"
bgcolor
="0xf4f4f4"
x
="1"
y
="1"
height
="${parent.height-2}"
width
="${parent.width-2}"
/>
</
class
>
class
标签是自定义类的基础,name="bordergreenbox" 属性为类命名,extends="view" 声明 “bordergreenbox”类是继承自“view”(view 是最常用的类,用于组织包含在其中的元素的交互),相应的也就继承了“view”所有的属性和方法。定义宽度时用到了“${}”约束符,这意味着“bordergreenbox”的宽度要受到“canvas.width-2”的约束,接着设置背景为绿色 bgcolor="0x9ae900"。为了能达到绿色边框的效果,我们在其中嵌入另一个 view,再设置颜色、相对坐标、高、宽。
以下部分是在构造 newPeople 类,用于让用户输入基本信息:
<
class
name
="newPeople"
extends
="view"
fontsize
="13"
visible
="false"
>
<
simplelayout
spacing
="4"
axis
="x"
/>
<
text
>
姓名:
</
text
><
edittext
name
="userName"
width
="60"
/>
<
text
>
电话:
</
text
><
edittext
name
="userPhone"
width
="100"
/>
<
text
>
电子邮件:
</
text
><
edittext
name
="userEmail"
width
="140"
/>
<
text
>
地址:
</
text
><
edittext
name
="userAddress"
width
="230"
/>
</
class
>
和 bordergreenbox 类不同的是,newPeople 类引入了新属性 visible="false",它的作用是让newPeople 不可见,如果不设置该属性则默认为可见。我们还引入了 simplelayout 类,它的作用是均匀分布 newPeople 类中所有的对象,spacing="4" 是在定义间距为 4,axis="x" 定义按照横向分布。text 类是用来嵌入文字的,且支持嵌入 <a>、<b>、<font> 等 HTML 标记,而 edittext 的作用则类似 HTML 中的 input 标签。运行效果如图四。

图四:newPeople 类
如何访问 JSP 提供的数据源呢?在这个应用中,分别构建了两个数据集,db4odata 用来获取返回的列表数据,peopleoperate 进行业务操作,下面主要分析 db4odata:
<
dataset
name
="db4odata"
src
="peopleoperat.jsp?action=getPeopleData"
type
="http"
request
="false"
/>
<
datapointer
name
="db4odtpt"
xpath
="db4odata:/*"
>
<
handler
name
="ondata"
>
canvas.bgview.rowcontainer.datapath.setPointer(this.p);
canvas.loader.setVisible(false);
Debug.write("data loaded!");
</
handler
>
</
datapointer
>
dataset
是数据访问类,src 作为资源访问路径指向 JSP 页面,并规定type 为 http 协议,当request="false" 时,必须通过调用 doRequest() 方法才能访问资源并获取数据,而为“true”时,则会自动访问资源,一般应设置为“false”。接下来用到了 datapointer 类,datapointer 是 dataset 的数据指针,并遵循 XPATH 语法规范访问数据,xpath="db4odata:/*" 定义 XPATH 的路径,既 db4odata dataset 的所有子路径。handler 类捕获 ondata 事件(ondata 是 datapointer 对象内置事件),并通过“.”运算符为 bordergreenbox 类的实例 bgview 设置数据源,再隐藏一个名为 loader 的对象,代码如下:
<
text
name
="loader"
x
="2"
y
="2"
text
="loading
"
/>
不错,loader 对象仅仅写下“loading...”,这是为了改善程序的互动效果。接下来分析 bordergreenbox 类实例 bgview,bgview 包含了两个 view 对象。以下代码向我们展示了第一个 view,它的作用是提供列名:
<
view
x
="1"
y
="15"
fontsize
="14"
fgcolor
="0xFFFFFF"
fontstyle
="bold"
width
="${canvas.width-4}"
height
="25"
bgcolor
="0xdd8910"
>
<
text
x
="40"
>
ID
</
text
>
<
text
x
="140"
>
姓名
</
text
>
<
text
x
="260"
>
电话
</
text
>
<
text
x
="410"
>
电子邮件
</
text
>
<
text
x
="620"
>
地址
</
text
>
</
view
>
第二个 view 对象 rowcontainer,刚才我们分析 db4odata 数据源集时捕获的 ondata 事件吗?不错,一旦接收到数据,rowcontainer 也就从 name="db4odtpt" 的数据指针中获取了数据:
<
view
name
="rowcontainer"
x
="1"
y
="39"
datapath
=""
width
="$once{parent.width-2}"
height
="$once{parent.height-40}"
clip
="true"
>
<
view
name
="columns"
datapath
="${parent.datapath}"
width
="$once{parent.width-2}"
>
<
simplelayout
axis
="y"
spacing
="1"
/>
<
selectionmanager
name
="selector"
toggle
="true"
/>
<
view
datapath
="*"
width
="$once{parent.width}"
bgcolor
="0xffffff"
onclick
="parent.selector.select(this);"
>
<
text
x
="0"
datapath
="@userId"
width
="110"
/>
<
text
x
="115"
multiline
="true"
datapath
="@userName"
width
="100"
/>
<
text
x
="220"
multiline
="true"
datapath
="@userPhone"
width
="130"
/>
<
text
x
="355"
multiline
="true"
datapath
="@userEmail"
width
="180"
/>
<
text
x
="540"
multiline
="true"
datapath
="@userAddress"
width
="220"
/>
<
method
name
="setSelected"
args
="amselected"
>
<![CDATA[
if (amselected) {
var bgcolor = 0xD5E4F3;
canvas.np.sendit.userid = this.datapath.xpathQuery('@userId');
canvas.np.userName.setText(this.datapath.xpathQuery('@userName'));
canvas.np.userPhone.setText(this.datapath.xpathQuery('@userPhone'));
canvas.np.userEmail.setText(this.datapath.xpathQuery('@userEmail'));
canvas.np.userAddress.setText(this.datapath.xpathQuery('@userAddress'));
canvas.np.userName.setAttribute('enabled',false);
} else {
var bgcolor = 0xFFFFFF;
canvas.np.userName.clearText();
canvas.np.userPhone.clearText();
canvas.np.userEmail.clearText();
canvas.np.userAddress.clearText();
canvas.np.userName.setAttribute('enabled',true);
}
this.setBGColor( bgcolor );
]]>
</
method
>
</
view
>
</
view
>
<
scrollbar
/>
</
view
>
clip="true"
属性是为了让包含在其中的子对象之间能够很好的布局。rowcontainer 中又嵌套了 view 对象 columns,并通过父对象取得了数据路径 datapath,接着按纵向均匀布局其中的行,selectionmanager 类似 HTML 中的 select 标签,一旦选中某行将触发为其内置属性 selected 赋值为“true”。接下来的 view 才是真正显示数据列表,onclick 事件也是 view 类的内置事件,当点击了某条记录,将联合 selectionmanager 进行操作,所以在这里为 selectionmanager 传入了选中的某条记录。datapath="@userId" 用于绑定 XML 数据中的 userId 属性,并自动显示。刚才说到由于 selectionmanager 和 view 是紧密联系的,所以在这里重写了 view 的 setSelected 抽象方法,并传入了布尔类型数据。在 <![CDATA[ ]]> 之间的代码,OpenLaszlo 将忽略其中的特殊字符(例如“<”符号),np 是之前我们构造的 newPeople 类的实例,接下来会讲到,而 canvas.np.sendit.userid = this.datapath.xpathQuery('@userId') 则是在进行赋值操作,虽然 newPeople 类的实例是不可见的,但却隐含的传入了数据,canvas.np.userName.setAttribute('enabled',false) 是动态创建的属性,为了区分“新增”、“修改”操作(新增可编辑“用户名”而修改则不能)。var bgcolor 是在声明局部变量,和 Java 语言不同的是,if 语句内声明的局部变量也能在语句之外调用,所以 this.setBGColor(bgcolor) 不会出现任何异常。
想必大家很关心 newPeople 类实例 np 是什么样子,接下来会讲解到如何添加和修改数据并提交给 JSP 处理:
<
newPeople
name
="np"
y
="85"
>
<
button
name
="sendit"
height
="24"
width
="40"
text
="提交"
>
<
attribute
name
="userid"
value
="0"
/>
<
attribute
name
="action"
value
="null"
/>
<
handler
name
="onclick"
>
<![CDATA[
var username = parent.userName.getText();
var usermobile = parent.userPhone.getText();
var useremail = parent.userEmail.getText();
var useraddress = parent.userAddress.getText();
var usernamenabled = parent.userName.getAttribute('enabled');
function send(action){
peopleoperate.setQueryParam('action',action);
peopleoperate.setQueryParam('userId',userid);
peopleoperate.setQueryParam('userName',username);
peopleoperate.setQueryParam('userPhone',usermobile);
peopleoperate.setQueryParam('userAddress',useraddress);
peopleoperate.setQueryParam('userEmail',useremail);
peopleoperate.doRequest();
canvas.loader.setVisible(true);
parent.setVisible(false);
canvas.bgview.setY(85);
}
if (username!='' && usermobile!='' && useremail!='' && useraddress!=''){
if (action == 'add' && usernamenabled == true){
Debug.write('addPeople');
send('addPeople');
} else if (action=='update'){
Debug.write('updatePeople');
send('updatePeople');
}
}
userid = 0;
]]>
</
handler
>
</
button
>
</
newPeople
>
在此我们引入了 button 类,接着构造了两个属性,userid 用于在修改时传递用户 ID,而 action 则用于确定是何种业务操作。接着捕获了 button 类的 onclick 事件,声明局部变量以便接下来发送,function send(action){…} 是构造的内部方法,用于发送请求,peopleoperate.setQueryParam() 是在对 peopleoperate 数据集创建请求参数,最后 peopleoperate.doRequest() 发出请求。构造好内部方法后,开始判断是哪种业务操作,并分别传入 'addPeople' 和 'updatePeople' 参数,待提交完成后 parent.setVisible(false) 隐藏 np。
我们在前面提到了当点击了 columns 对象中的某一行将自动为 np 对象填入值,而这个时候 np 是隐藏的,不错,需要增加一个按钮来显示 np 以便进行修改:
<
button
name
="updtuser"
x
="60"
y
="55"
height
="24"
width
="40"
text
="修改"
>
<
handler
name
="onclick"
>
parent.np.setVisible(true);
parent.bgview.setY(115);
canvas.np.sendit.action = 'update';
</
handler
>
</
button
>
新增和修改的操作差不多,而删除则传入用户 ID, peopleoperate.setQueryParam('action','delPeople') 直接删除。
到此,页面代码也完成得差不多了,启动您的服务器。运行效果如图五:

图五:
openlaszloWITHdb4o