本栏目责任编辑: 闻翔军
数据库及信息管理
在 Web 表现层分离表现与数据方法的探讨
( 武汉理工大学 计算机科学与技术学院, 湖北 武汉 430080)
高凌 1, 熊前兴 2
摘要: 本文简要介绍了 XMLHttpR equest 对象, 随后详细阐明了如何在 web 应用中利 用 XMLHttpR equest 结 合 XML 在 表 现 层 实 现
表现与数据的分离的方法。并在文章结尾阐述了该方法在 web 应用中的广泛前景。
关键词: XMLHttpR equest 对象; Ajax; 数据分离
中图分类号: TP393.02
文献标识码: A
文章编号: 1009- 3044(2007)02- 10301- 02
Res earch on the Pres entation and Data Separating in the Web Pres entation Layer
GAO Ling1,XIONG Qian- Xing2
(Wuhan University of Technology,Wuhan 430080,China)
Abs tract:This paper introduce the XMLHttpR equest Object and illustrate how to separate Presentation and Data in the presentation layer
of a web application with the XMLHttpR equest and XML.
Key words :XMLHttpR equest Object;Ajax;Separate data
1 引言
在 web 应用的发展过程中, Model2(MVC)架构的 出 现, 分 离 了
model1 中 混 杂 的 业 务 逻 辑 与 表 现 , 但 有 一 个 问 题 始 终 困 扰 着 我
们, 即表现层中的表现与数据仍混杂在一起。例如: 在传统 web 开
发 中 , 使 用 来描述一个 html text 控 件 。 其 中, value=”321”表 示 该 控 件
的值, class="input"描述了该控件在浏览器中显示的外观。这样的
传 统 方 法 虽 然 简 单 一 目 了 然 , 但 它 的 缺 点 也 是 显 而 易 见 的 : 表 现
与 数 据 值 混 杂 , 一 旦 这 样 的 值 要 被 服 务 器 传 递 的 另 一 个 值 所 替
代, 页面就要被刷新。本文探讨了一种新的模式, 使得 html 控件与
数据分离, 控件与数据的绑定工作交给浏览器完成。
2 XMLHttpReques t 对象概述
XmlHttp 是 一 套 可 以 在 Javascript、VbScript、Jscript 等 脚 本 语
言 中 通 过 http 协 议 传 送 或 从 接 收 XML 及 其 他 数 据 的 API[1]。
XmlHttp 最大的用处是可以更新网页的部分内容而不需要刷新整
个页面。
现在的绝对多数浏览器都增加了对 XmlHttp 的支持, IE 中 使
用 ActiveXObject 方 式 创 建 XmlHttp 对 象, 其 他 浏 览 器 如 : Firefox、
Opera 等通过 window.XMLHttpRequest 来创建 xmlhttp 对象。在使
用 XMLHttpRequest 对 象 发 送 请 求 和 处 理 响 应 之 前 , 必 须 先 用
javascript 创 建 一 个 XMLHttpRequest 对 象 , 由 于 XMLHttpRequest
不 是 一 个 W3C 标 准 , 所 以 要 采 用 多 种 方 法 来 创 建 XMLHttpRe-
quest 的实例。代码如下:
var xmlHttp;
//创建 XMLHttpRequest 对象
function createXMLHttpRequest(){
/*
为保证浏览器兼容性, 做如下处理。
如 果 浏 览 器 支 持 ActiveX 对 象 , 则 用 ActiveX 来 创 建 XML-
HttpRequest 对象, 否则使用本地 JavaScript 对象技术来创建
*/
if(window.ActiveXObject){
xmlHttp=new ActiveXObject("Microsoft.XMLHTTP");
}
else if(window.XMLHttpRequest){
xmlHttp=new XMLHttpRequest();
}
}
从上面代码可以看出, 在 IE 中把 XMLHttpRequest 实现为一
个 ActiveX 对象, 其它浏览器把它作为一个本地 JavaScript 对象。
3 表现与数据分离方法的探讨
3.1 表现与数据分离的方法
本文对 web 应用的表现层进行了分析, 将表 现 层 拆 分 为 html
页面模块、数据模块、javascript 客户端模块三个模块。
( 1) html 页面模块。
该模块由 html 控件及其他诸如 css 等组成。对于 web 程序开
发人员, 更关心数据的承载部分以及少量的页 面 修 饰 。 而 网 页 的
数据承载, 是通过 html 控件的形式表现的。如
,,表单等等。将这类控件以不带值得形
式表现出来, 就构成了 html 页面模块。例如:
( 2) 数据: 通常由服务器 从 数 据 库 中 读 取 。 以 XML 为 最 终 表
现 形式。因此, 可以通过一个 java servlet 来取得数据库中的值, 然
后将其格式化为标准的 xml 输出。
( 3) javascript 客户端: 用于绑定 html 控 件( 或 称 为 html 对 象)
和数据。上面已经得到了 html 控件和数据,接 下 来 最 关 键 的 问 题
是 如 何 让 数 据 显 示 在 正 确 的 位 置 。 这 需 要 用 到 XMLHttpRequest
对 象 。 首 先 , 声 明 一 个 XMLHttpRequest 对 象 xmlHttp=new Ac-
tiveXObject("Microsoft.XMLHTTP")。随后, 建立对模块②中生成的
数据的调用: xmlHttp.open("Get",url,true)。最后, 取得这样的服务器
数据并与 html 控件绑定。
Html 控 件 值 = xmlHttp.responseXML.getElementsByTagName
(“数据库字段名”)
document.getElementById ("html 控 件 ID").innerHTML= Html
控件值
收稿日期: 2006- 10- 02
基金项目: 湖北省交通厅水路规费外网建设项目
作者简介: 高凌( 1982- ) , 男, 湖北浠水人, 武汉理工大学科学与技术学院硕士研究生, 研究方向为计算机支撑技术; 熊前兴( 1943- ) , 男, 博导,
研究方向为计算机支撑技术, 电子商务, Web 数据集成等。
301
数据库及信息管理
本栏目责任编辑: 闻翔军
经过上述的三个拆分, 我们用在 Web 表现层分离表现与数据
的思想实现了一个传统的 web 页面编程的工作( 流程见图 1) 。
称,全年通航里程,季节通航里程,不通航里程,总计里程,航道属性,船
闸数量,升船机数量,其它建筑物数量,总计建筑物数量 等对象
Varid,name,jumpingOffPoint,endPoint,aOnTraffic,sOnTraffic,
nOnTraffic,sumOnTraffic,
seaRouteAttribute,gate,osMachine,otherBuilding,sumBuilding;
取 值 的 第 二 步 是 对 上 面 定 义 的 js 变 量 赋 值 。 我 们 使 用 xml-
Http 取出 xml 中的数据。然后把该数据赋值给 js 变量。
id=xmlHttp.responseXML.getElementsByTagName (" 序 号")[0].
firstChild.nodeValue;
name =xmlHttp.responseXML.getElementsByTagName (" 航 道 名
图 1
称")[0].firstChild.nodeValue;
模块①可分离出来单独由 html 页面编写人员处理, web 程 序
员 只 关 心 业 务 逻 辑 , 关 心 数 据 如 何 从 数 据 库 到 模 型 层 , 再 到 业 务
层, 最后变成规范的 XML 格式。
3.2 表现与数据分离方法的具体实现
下面以湖北港航电子政务系统- - - 航道模块为例, 从数据库
中的航道明细表中得到: 航道名称、航道起点等信息。并由此计算
出航道里程总计和通航建筑物数量总计。其中, 航道里程总计=全
年通航航道里程+季节通航航道里程+不通航航道里程; 通航建筑
物数量总计=通航船闸数量+通航升船机数量+其它通航数量;
<湖北省航道情况表>
<序号>1
<航道名称>长江
<航道起点>段窑断面
<航道终点>鳊鱼溪
<全年通航航道里程>1037.90
<季节通航航道里程>10
<不通航航道里程>0
<航道里程总计>1047.90
<航道属性>天然
<通航船闸数量>3
<通航升船机数量>1
<其它通航数量>1
<通航建筑物数量总计>5
服务器端生成如上所示的 XML 文件后送给浏览器。
同时, 在浏览器端, 我们得到 html 页面
序号:
名称:
航道 起 点:
此 时 , 页 面 与 数 据 都 具 备 了 , 下 一 步 工 作 是 如 何 使 用
javascript 绑定值与对象。
首先是取值: 从经过业务逻辑处 理 得 到 的 XML 文 件 中 将 值
解析出来, 赋给 javascript 变量。
取值的第一步, 创建序号,航道名称,航道起点名称,航道终点名
302
电脑知识技术
jumpingOffPoint =xmlHttp.responseXML.getElementsByTagName
("航道起点")[0].firstChild
.nodeValue;
endPoint=xmlHttp.responseXML.getElementsByTagName(" 航 道
终点")[0].firstChild.nodeValue;
aOnTraffic=xmlHttp.responseXML.getElementsByTagName (" 全
年通航航道里程")[0].firstChild
.nodeValue;
sOnTraffic=xmlHttp.responseXML.getElementsByTagName (" 季
节通航航道里程")[0].firstChild
.nodeValue;
nOnTraffic=xmlHttp.responseXML.getElementsByTagName (" 不
通航航道里程")[0].firstChild
.nodeValue;
sumOnTraffic =xmlHttp.responseXML.getElementsByTagName ("
航道里程总计")[0].firstChild
.nodeValue;
seaRouteAttribute =xmlHttp.responseXML.getElementsByTag-
Name("航道属性")[0].firstChild
.nodeValue;
gate=xmlHttp.responseXML.getElementsByTagName(" 通 航 船 闸
数量")[0].firstChild.nodeValue;
osMachine=xmlHttp.responseXML.getElementsByTagName (" 通
航升船机数量")[0].firstChild
.nodeValue;
otherBuilding =xmlHttp.responseXML.getElementsByTagName ("
其它通航数量")[0].firstChild
.nodeValue;
sumBuilding =xmlHttp.responseXML.getElementsByTagName ("
通航建筑物数量总计")[0]
.firstChild.nodeValue;
取 值 的 工 作 完 成 后, 接 下 来 是 对 html 表 单 控 件 赋 值 : 将 上 述
得到的值与 Html 控件进行绑定。
document.getElementById("id").innerHTML=id;
document.getElementById("name").innerHTML=name;
document.getElementById ("jumpingOffPoint").setAttribute("val-
ue",jumpingOffPoint);
document.getElementById ("endPoint").setAttribute ("value",end-
Point);
document.getElementById ("aOnTraffic").setAttribute ("value",
aOnTraffic);
document.getElementById
("sOnTraffic").setAttribute ("value",
sOnTraffic);
document.getElementById ("nOnTraffic").setAttribute ("value",
nOnTraffic);
document.getElementById ("sumOnTraffic").setAttribute ("value",
sumOnTraffic);
document.getElementById ("seaRouteAttribute").innerHTML =
seaRouteAttribute;
(下转第 309 页)
本栏目责任编辑: 闻翔军
IF len(alltrim(thisform.text1.value))! =0
tiaojian=tiaojian+".and."+"dzbh="+" '
value)+"'
"
ENDIF
IF len(alltrim(thisform.text2.value))! =0
tiaojian=tiaojian+".and." +"xm=" +" '
value)+"'
"
ENDIF
IF len(alltrim(thisform.text3.value))! =0
tiaojian=tiaojian+".and."+"szdw="+" '
"+alltrim(thisform.text1.
" +alltrim(thisform.text2.
"+alltrim(thisform.text3.
value)+"'
"
ENDIF
thisform.dataenvironment.cursor1.filter=tiaojian
thisform.refresh
5.2.2 程序代码说明
单 选 按 钮 组 的 Name 属 性 为 默 认 值 Optiongroup1 ( 参 见 2.2
节 ) , 在 程 序 中 , 若 thisform.optiongroup1.value=1, 则 表 明 选 中 了 单
选按钮组中的第一个单选按钮, 即选中“全部”。同理, thisform.op-
tiongroup1.value=2 表明选中第二个单选按钮 “学生”, thisform.op-
tiongroup1.value=3 表明选中第三个单选按钮“非学生”。
组合框的 Name 属性为默 认 值 Combo1( 参 见 2.2 节) , 在 程 序
中, 若 thisform.combo1.listindex=1, 则 表 明 选 中 了 组 合 框 的 下 拉 列
表 中 的 第 一 个 列 表 项 , 即 选 中 “ 不 限 ”。 同 理 , thisform.combo1.
listindex=2 表明选中组合框的下拉列表中的第二个列表项 “男”,
thisform.combo1.listindex=3 表 明 选 中 组 合 框 的 下 拉 列 表 中 的 第 三
个列表项“女”。
读者编号、姓名、所在单位对应的文本框的 Name 属性分别为
Text1、Text2、Text3( 参 见 2.2 节) 。 要 判 断 这 些 文 本 框 是 否 有 希 望
输入的数据, 可先提取这些文本框的内容, 再去掉前后空格, 然 后
再检查剩余部分的长度是否为 0。长度为 0 表示文本框中的内容
是空字符串或无输入内容, 长度不为 0 表示有希望的数 据 输 入 。
在程序中, 提取 Text1 文本框的内容用 thisform.text1.value, 去掉前
后 空 格 用 alltrim()函 数 , 计 算 字 符 串 长 度 用 len()函 数 , 这 样 , len
(alltrim(thisform.text1.value))! =0 表 示 读 者 编 号 对 应 的 文 本 框 ( 即
text1 文 本 框 ) 有 希 望 输 入 的 数 据 。 同 理 , len(alltrim(thisform.text2.
value))! =0、len(alltrim(thisform.text3.value))! =0 分别表示姓名、所在
单位文本框有希望输入的数据。
程 序 及 查 询 流 程 图 ( 参 见 图 2) 中 的 sfxs、xb、xm、szdw 分 别 对
(上接第 302 页)
document.getElementById("gate").setAttribute("value",gate);
document.getElementById ("osMachine").setAttribute ("value",os-
Machine);
document.getElementById ("otherBuilding").setAttribute ("value",
otherBuilding);
document.getElementById ("sumBuilding").setAttribute("value",
sumBuilding);
演示结果
数据库及信息管理
应于 duzhe.dbf 数据表中的字段名, 其含义分别为是否学生、性别、
姓名、所在单位( 参见表 1) 。另外, 程序中的 tiaojian 表示设定的内
存变量, 表示查询条件。
程 序 中 thisform.dataenvironment.cursor1.filter=tiaojian 用 于 将
数 据 环 境 中 对 应 于 数 据 表 duzhe.dbf 的 游 标 cursor1 的 Filter 属 性
设置为 tiaojian, 而 thisform.refresh 用于刷新表单。
5.3 退出查询
如 前 所 述 , 表 单 运 行 后 , 若 单 击“退 出 ”按 钮 , 则 释 放 表 单 , 退
出查询功能模块。所以, 可以在“退出”命令按钮控件的 Click 事件
中添加如下 VFP 程序代码: thisform.release。
6 进一步说明
(1) 选项按钮组或组合框对象都可以用于有限可选 项 的 单 项
选择。对于查询范围及性别的选择, 由于它们都具有 有 限 的 可 选
项, 并 且 各 自 的 可 选 项 都 是 相 互 排 斥 的 , 所 以 都 可 以 在 设 计 时 利
用选项按钮组对象的控件或组合框对象的控件来实现。本文为阐
明 这 两 种 控 件 的 用 法 , 分 别 使 用 了 选 项 按 钮 组 控 件 和 组 合 框 控
件。
(2)若进行精确查询, 则需要进行精确比较。这时可在 表 单 的
Load 事件中添加代码: set exact on, 并在表单的 Unload 事件中添
加代码: set exact off。
(3) 有时单击文本框对象时, 希望将文本框中原有 的 内 容 清
除, 以方便使用。这时可以在文本框控件的单击事件 中 将 此 文 本
框的 value 属性设置为空白字符串。例如, 运行查询表单过程中,
单击 Text1、Text2 及 Text3 文本框时, 希望将上次操作输入的内容
清 除 , 从 而 可 以 直 接 输 入 新 值 , 为 达 此 目 的 , 可 以 分 别 在 Text1、
Text2、Text3 文本框控件的 Click 事件中添加 如 下 代 码: this.value=
space(0)。
(4)有时希望鼠标移到某对象上时能够显示提示文字, 就 需 要
分 别 设 置 表 单 的 ShowTips 属 性 和 此 对 象 控 件 的 ToolTipText 属
性 。 例 如, 可 以 将 表 单 的 ShowTips 属 性 设 置 为“.T.- 真 ”, 将 Text1
文本框控件的 ToolTipText 属性值设置为“此文本框为空白值或无
输 入 数 据 时, 表 示 查 询 时 不 对 读 者 编 号 做 任 何 限 制 ”, 将 Text2 文
本框控件的 ToolTipText 属性值设置为“此文本框为空白值或无输
入 数 据 时, 表 示 查 询 时 不 对 姓 名 做 任 何 限 制 ”, 将 Text3 文 本 框 控
件的 ToolTipText 属性值设置为“此文本框为空白值或无输入数据
时, 表示查询时不对所在单位做任何限制”。
4 结束语
通 过 以 上 工 作 , 在 web 表 现 层 分 离 了 表 现 与 数 据 , 降 低 了
html 对象与数据的强耦合, 使得网页设计人员与程 序 员 的 分 工 界
线更加明确。网页开发人员只用关注 html 页面的设计, 程序员只
关心从数据取出到生成 XML 的业务逻辑过程。数据与 html 控件
的绑定是有规律可循的, 可以被提取为通用的 JS 组件。
参考文献:
[1]Ryan Asleon,Nathaniel T.Schutta.Foundations of Ajax.ISBN:
7- 115- 14481- 8,人民邮电出版社.2006( 2) .
[2]Gay S.Horstmann,Gary Cornell 著 , 程 峰 , 黄 若 波 译.java2 核
心 技 术, java2 高 级 特 性.ISBN: 7- 111- 12543- 6. 机 械 工 业 出 版 社.
2004( 6) .
[3]Dave Crane,Eric Pascarello, Darren James 著.Ajax in Action.
ISBN: 1- 932394- 61- 3.2006( 4) .
[4]李刚, 宋伟, 邱哲.Ajax+Lucene 构建搜索引擎.ISBN: 7- 115-
14707- 8.人民邮电出版社.2006( 4) .
309