基于ajax的IETM动态目录树的设计与实现
树形目录是IETM应用系统中常用的直接访问方式,用户通过清晰的、可折叠的树形目录可以快速定位到目标卡片或章节。这样做有两个优势:一是直观,通过树节点的展开、折叠可以很直观地了解到数据之间的层次关系。二是方便,通过点击树的节点就可以显示所需要的信息,而无需查询操作。常见的目录树有两种类型:
一是静态目录树。在应用程序开始时一次性加载所有数据。优点:纯客户端控制,页面无需刷新。缺点:(1)一架飞机的技术手册数量繁多,所构成目录树中的条目最少也有上千条。庞大的数据量使页面显示速度迟缓,多到一定程度时,会让客户端超时以至于读取失败,严重时甚至会使IE当掉。(2)对于某个终端用户来说,经常使用的只是特定的某几类技术资料,目录中的绝大部分信息是不需要的。
二是动态目录树。每次只加载一级目录。只有用户展开节点时,才加载下一级节点数据。优点:数据量小,响应速度快。缺点:页面频繁刷新,用户体验差,而且刷新后会使其它数据丢失。
本文提出了一种新的解决思路:采用ajax技术构建动态的目录树。这种方法综合了前两种方法的优点,一方面数据动态加载,一次只加载一级目录,只有用户点击后才显示下一级分类目录。不仅大大减少了数据的冗余,提高了页面加载速度,而且使得每个用户都能根据实际需要构建出满足自身要求的目录树,灵活性强。另一方面采用ajax技术实现与后台服务器之间的数据交换,使得页面无需全部刷新。
1.Ajax技术简介
Ajax的全称是Asynchronous JavaScript and XML,2005年2月由Adaptive Path公司提出,之后,在Google地图中得到了成功的应用,发展迅速。实际上,Ajax并不是一种新技术,而是几种技术的组合,包括:
●基于XHTML和CSS标准的表示;
●使用DocumentObjectModel进行动态显示和交互;
●使用XMLHttpRequest与服务器进行异步通信;
●使用JavaScript绑定一切。
归纳起来说,它有两大特点:
一是异步读取数据。Ajax技术的核心是XMLHttpRequest对象,它提供了客户端与服务器端异步通信的能力,直接通过浏览器与后端服务器进行通信,在服务器端进行数据的处理(如更新或查询数据库)。
二是页面无需全部刷新。从后台服务器返回的数据,通过JavaScript和CSS来更新页面中相应的区域。可以看出,ajax使Web应用程序无需从服务器重新载入页面来实现页面数据的更改,从而避免了传统web应用程序在提交请求后出现的“白屏”现象。
2. IETM系统中目录树结构的构建
2.1目录文档的结构 在IETM应用系统中,技术资料以数据模块的形式存储在数据库中,每个数据模块对应一个xml文档。出版物模块则用来表示各个数据模块所属的类别和层次关系,相当于技术资料的总目录。按照S1000D标准,出版物模块的数据架构与相应的XML文档见图1。
可以看出,出版物模块中的核心信息有两类:一类是数据模块的分类信息,这一信息是通过元素pmentry之间的嵌套关系体现出来的;另一类是数据模块的内容信息,元素refdm中包含该数据模块的唯一编码。元素title根据位置的不同,代表不同的含义。如果title的下一个元素为pmentry,表示所属类别的名称;如果title的下一个元素为dmentry,表示数据模块的名称。
2.2目录树的结构 在IETM阅读系统中,目录树用于对所有技术资料进行组织、管理和导航。它描述了技术数据的总体结构框架,为用户展示了清晰的、层次分明的数据结构组织形式。目录树的结构如图2所示,基本组成单元是节点。IETM系统目录树中节点的类型又可以分成两种:父节点和叶子节点。叶子节点表示IETM系统技术资料对应的数据模块,父节点表示数据模块的所属类别。
2.3目录文档与目录树之间的映射 出版物文档中的元素pmentry、dmentry分别对应树中的父节点和叶子节点。为了使目录文档与树之间的对应关系更加清晰,便于使用xsl文件进行转换,需要对其它信息进行归类,并转换为所属元素的对应属性,使目录文档中只包含pmentry和dmentry两类元素。图3是转换后的xml文档,可以看出它的层次信息十分清晰,与树结构一一对应。
3. 基于ajax的动态目录树的实现
3.1业务逻辑 Ajax动态目录树的业务逻辑图4所示。
(1)前台浏览器端捕捉用户的操作事件。如果要展开节点,则通过xmlHTTP异步调用服务器端,并将所要展开节点的路径作为参数传递到服务器端。
(2)后台服务器端根据客户端传送的节点参数,求取所有子节点的参数,包括显示的文字、有无下级节点。如果是叶子节点,那么还要计算该节点所链接数据模块的URL地址。
(3)将获取的数据通过response写回去。
(4)客户端接受服务器端的数据,将子节点的参数添加到所展开节点下面。
3.2关键设计
3.2.1客户端创建xmlHttp对象 客户端通过xmlHttp对象调用服务器,传送待展开节点的路径,并发出HTTP请求,代码如下:
if(type of XMLHttpRequest!="undefined") //如果是IE浏览器,创建XMLHttpRequest对象
{xmlhttp=newXMLHttpRequest();}
else if(typeof ActiveX Object!="undefined")//否则创建XMLHTTP ActiveX对象。
{xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");}
else return null;
xmlhttp.open("post","pm.aspx",true);//调用服务器
xmlhttp.onreadystatechange=function(){
if(req.readyState==4){
var xmlNodes=req.responseXML.
firstChild;//处理回传数据
LoadDemandNodes(xmlNodes,node);
} }
xmlhttp.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
xmlhttp.send("nodePath="+nodePath);//传送待展开节点的路径
3.2.2服务器端处理数据 服务器端调用存储过程从数据库中取出出版物模块,并根据客户端传来的待展开节点的路径对出版物模块进行过滤,取出子节点的数据,组织成特定格式的XML文档后返回到客户端。
为了表示目录树中的节点,在服务器端创建xml元素entry,包含三个属性,见表1。同时,将出版物模块中的元素pmentry和dmentry都转换为entry元素,并通过Response.Write将数据返回。
服务器端数据处理的代码如下:
private void StreamNodeData(XmlNode node){
this.Response.ContentType="text/xml";
StringBuilder sb=new StringBuilder();
sb.Append("<entries>");
foreach(XmlNode child in node.ChildNodes){
if(child.Name=="pmentry")//如果是pmentry,则entry的属性children为true,表示父节点。
sb.AppendFormat("<entryname='{0}'children='{1}'/
>",child.Attributes["title"].Value,"true");
else //如果是dmentry,则entry的属性children为false,表示叶子节点,同时将该数据模块的dmc值赋予属性url。
sb.AppendFormat("<entryname='{0}'children='{1}'
url='{2}'/>",child.Attributes["title"].Value,"false",child.Attributes["
dmc"].Value);
}
sb.Append("</entries>");
this.Response.Write(sb.ToString());
this.Response.Flush();
this.Response.Close();}
3.2.3客户端更新HTMLDOM 客户端通过xmlhttp.responseXML接受服务器端返回的数据并解析,从而更新HTML页面中相应位置的数据。为了表示父节点,人为加上一个节点显示Loading...,并将节点闭合,以显示与叶子节点的区别。
客户端实现HTMLDOM更新的代码如下:
function LoadDemandNodes(xmlNodes,node){
for(vari=0;i<xmlNodes.childNodes.length;i++){
var currentRecord=xmlNodes.childNodes[i];
var url=currentRecord.getAttribute("url");
var newChild=node.addChild(xmlNodes.childNodes[i].
getAttribute("name"));//添加子节点
if(newChild!=null&&xmlNodes.childNodes[i].getAttribute("
children")=="true"){//如果是父节点,添加一个子节点。
newChild.addChild("Loading...");
newChild.setExpanded(false);}
else{ //如果是叶子节点,设置url属性。
newChild.element.childNodes[2].setAttribute("URL",url) }
}
}
3.3实现结果 通过上述方法,我们设计了某型飞机IETM阅读系统,见图5。系统中数千个数据模块的数据是分级动态加载的,页面加载速度快,并且页面的局部刷新实时稳定。
实践证明,以上方法设计的目录树具有高效、快速、用户体验好等优点,是一种比较理想的树型目录设计方法。