分享
 
 
 

WEB工程实现清晰VIEW-Server分层

王朝other·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

你也许已经是一个PHP的高手或老手了.或许你已经在PHP项目中经验丰富.如果是这样的话,你是不是更加感觉PHP的工程中使用层次结构的难度?在这篇文章中,我通过对JPSpan一点粗浅的研究,谈谈如何在WEB工程实现GUI设计的方法.

1.使用DHTML

WEB页面的编写是一件很复杂的事情,不管是从审美角度还是从功能角度来看,页面的设计师也许很关注如何使用图片以及CSS使得他看起来象一副画.然而,我们从工程角度,追求的最大目标是,象写JAVA/C++代码一样生成我们需要的界面.打开一个页面的源文件,大多被HTML标签所充斥.不管是否漂亮,每每都杂乱无章.DHTML语言,毫无疑问,就是动态HTML语言,可以使得我们动态生成WEB页面的内容.他使得我们可以利用JavaScript语言构建一个生成页面元素的函数库.在功能界面中,我们最关心的是FORM,因为在FORM中有我们需要的交互控件.我们最常使用的是三类Select,Text,Button.

其次,我们的动态页面生成的过程基于以下几步:

1.设计一个页面的总体框架,这一部分我们可以静态的写在body区内,可以对整个页面进行一个分配.一般我们使用 table来构成.其中最关键的是div标签,下面的例子中,我们就可以看出界面分为三部分:

用于数据存储的不可见区域 id=invisible

用于显示交互控件并进行交互的的区域 id=disarea

用于显示结果表格的区域(我们使用table的形式显示结果) id=tablearea

<body leftMargin=50 topMargin=10 onload='initView()'>

<table>

<!--------------------------------------数据储存区------------------------------------->

<tr colspan="2">

<div id="invisble" style="display:none;">

</div>

</tr>

<tr>

<td><div id="disarea"></div></td>

<td width="100" align="center"> => </td>

<td><div id="tablearea"></div></td>

</tr>

</table>

</body>

2.注意到整个界面的初始化工作的入口就是body标签中的onload='initView()',我们看看initView:

/**

* 初始化界面函数

*/

function initView(){

/*----创建Form----*/

createForm(fmName,'教员信息管理','disarea');

/*----在Form中添加元素----*/

addElement(fmName,'select','dqxx','当前学校:',true);

addElement(fmName,'text','yszxx','原所在学校:',false);

addElement(fmName,'text','jyxm','教员姓名:',true);

addElement(fmName,'select','zzmm','政治面貌:',true);

addElement(fmName,'select','mz','民族:',true);

addElement(fmName,'text','cjgzsj','参加工作时间:',true);

buts = {'csh':'button','zj':'button','sc':'button','cx':'button'};

addGroup(fmName,buts,'按钮:',false);

/*----------设定元素的属性和值以及事件响应--------------*/

setElementValue(fmName,'csh','初始化');

setElementValue(fmName,'zj','增加');

setElementValue(fmName,'sc','删除');

setElementValue(fmName,'cx','查询');

setElementClick(fmName,'csh',cshClick);

setElementClick(fmName,'zj',zjClick);

setElementClick(fmName,'sc',scClick);

setElementClick(fmName,'cx',cxClick);

initForm();

}

initView有点需要说明:createForm,addElement,addGroup,setElementValue等函数都是被定义在一个 函数库utils.js中的函数.(最后会把utils.js贴上)

3.initForm()函数是对界面的必要的控件的数据的初试化.在这个函数中,我们用到了和服务器的交互.

/**

* 代码-控件对照表

*/

var CodeToElement = {'xx':'dqxx','zzmm':'zzmm','mz':'mz','xb':'xb','drfw':'drfw','xl':'xl','zc':'zc',

'gzdc':'gzdc','xzzw':'xzzw','ygzt':'ygzt','ygsf':'ygsf'};

/**

* 给Form初始化数据,一般

*/

function initForm(){

for(key in CodeToElement){

var xmlReader = new menureader(cbHandler);

xmlReader.readcodes(key);

}

}

/**

* 读取服务器的数据的回调函数

*/

var cbHandler={

readcodes:function(res){

//alert(res);

fillOptions(res);

}

}

其中,CodeToElement数组定义了界面需要代码表的名称和我们界面控件名称的一个对照表.这些控件都是Select下拉框,可供用户选择的代码.在服务器端,我们定义了代码表XML文件就像

<?xml version="1.0" encoding="UTF-8"?>

<codes>

<code name="xb" mean="性别">

<element name="男" value="0"/>

<element name="女" value="1"/>

</code>

<code name="xx" mean="学校代码表">

<element name='学校1' value='0'/>

<element name='学校2' value='1'/>

</code>

<code name="mz" mean="民族代码">

<element name='汉' value='0'/>

<element name='蒙' value='1'/>

</code>

<code name="xl" mean="学历">

<element name='大专' value='0'/>

<element name='大本' value='1'/>

</code>

<code name="zc" mean="职称">

<element name='小高' value='0'/>

<element name='中高' value='1'/>

</code>

</codes>

在服务器端我们定义了类MenuReader,他会读取该XML文件,返回给前台,得益于JPSpan提供的结构,因此我们可以在Javascript代码中定义MenuReader对象,并在JS中调用MenuReader的readcodes方法,并且让cbHandler作为回调函数来处理返回的结果,我们的处理就是把结果集设置到所有的select下拉框中.我们先不理会后台如何完成工作,观察前台,我们便能察觉,该WEb页面的工作方式,已经变成了标准的View-Server结构,而不需要在前台有任何PHP脚本,也不需要有任何的页面Reload.完全在一个页面就完成所有的交互的工作,实现了一次加载,多次交互的功能.

4.对于所有的服务器代码我们在下一篇文章中解释,下面,我们看看前台的完整代码.

<!--------------------------jyxxgl.php----------------------------------->

<!--------------------------jyxxgl.php----------------------------------->

<html>

<head>

<title>教员信息管理</title>

<link href="main.css" rel="stylesheet">

<!--------------------------------用例定义的Javascript函数----------------------------------->

<script type="text/javascript" src='utils.js'></script>

<script type="text/javascript" src="viewServer.php?client"></script>

<script type="text/javascript">

/**

* ----定义常量----

*/

var fmName = 'fmJyxx';

/**

* 初始化界面函数

*/

function initView(){

/*----创建Form----*/

createForm(fmName,'教员信息管理','disarea');

/*----在Form中添加元素----*/

addElement(fmName,'select','dqxx','当前学校:',true);

addElement(fmName,'text','yszxx','原所在学校:',false);

addElement(fmName,'text','jyxm','教员姓名:',true);

addElement(fmName,'select','zzmm','政治面貌:',true);

addElement(fmName,'select','mz','民族:',true);

addElement(fmName,'text','cjgzsj','参加工作时间:',true);

buts = {'csh':'button','zj':'button','sc':'button','cx':'button'};

addGroup(fmName,buts,'按钮:',false);

/*----------设定元素的属性和值以及事件响应--------------*/

setElementValue(fmName,'csh','初始化');

setElementValue(fmName,'zj','增加');

setElementValue(fmName,'sc','删除');

setElementValue(fmName,'cx','查询');

setElementClick(fmName,'csh',cshClick);

setElementClick(fmName,'zj',zjClick);

setElementClick(fmName,'sc',scClick);

setElementClick(fmName,'cx',cxClick);

initForm();

}

/**

* 代码-控件对照表

*/

var CodeToElement = {'xx':'dqxx','zzmm':'zzmm','mz':'mz','xb':'xb','drfw':'drfw','xl':'xl','zc':'zc',

'gzdc':'gzdc','xzzw':'xzzw','ygzt':'ygzt','ygsf':'ygsf'};

/**

* 给Form初始化数据,一般

*/

function initForm(){

for(key in CodeToElement){

var xmlReader = new menureader(cbHandler);

xmlReader.readcodes(key);

}

}

/**

* 读取服务器的数据的回调函数

*/

var cbHandler={

readcodes:function(res){

//alert(res);

fillOptions(res);

}

}

/**

* 根据返回的数据填充Select域

*/

function fillOptions(res){

var cn = res[0];

var elemName = getElemByCode(cn);

var cvArr = res[1];

for(var i=1;i<cvArr.length;i++){ //str 格式: [name:sd][value:1]

var str = cvArr[i];

pos = str.indexOf("]");

name = str.substring(str.indexOf(":")+1,pos)

str = str.substring(pos+1,str.length);

pos = str.indexOf("]");

value = str.substring(str.indexOf(":")+1,pos);

addSelectOption(fmName,elemName,name,value);

}

}

/**

* 通过代码的名称取得对应的元素名称

* 是架设代码和前台数据一致的方法

*/

function getElemByCode(codeName){

var cnLc = codeName.toLowerCase();

var cnArr = {'xx':'dqxx','zzmm':'zzmm','mz':'mz','xb':'xb','drfw':'drfw','xl':'xl','zc':'zc',

'gzdc':'gzdc','xzzw':'xzzw','ygzt':'ygzt','ygsf':'ygsf'};

return cnArr[cnLc];

}

/**

* 初始化按钮按下的处理

*/

function cshClick(){

alert('初始化');

}

/**

* 增加按钮按下的处理

*/

function zjClick(){

alert('增加');

}

/**

* 删除按钮按下的处理

*/

function scClick(){

alert('删除');

}

/**

* 查询按钮按下的处理

*/

function cxClick(){

alert('查询');

}

/**

* 遍历Form中的每一个元素

*/

function overGo(){

var elems = document.forms[fmName].elements;

for(i=0;i<elems.length;i++){

if(elems[i].type != 'button' && elems[i].type != 'submit' && elems[i].type != 'reset'){

alert(elems[i].name);

}

}

}

/**

* 把Form的数据封装成一个数组,数组的下标为名称

*/

function encapData(){

var elems = document.forms[fmName].elements;

var arr = new Array(elems.length);

for(i=0;i<elems.length;i++){

arr[elems[i].name] = elems[i].value;

}

return arr;

}

</script>

</head>

<body leftMargin=50 topMargin=10 onload='initView()'>

<table>

<!--------------------------------------数据储存区------------------------------------->

<tr colspan="2">

<div id="invisble" style="display:none;">

</div>

</tr>

<tr>

<td><div id="disarea"></div></td>

<td width="100" align="center"> => </td>

<td><div id="tablearea"></div></td>

</tr>

</table>

</body>

</html>

其中建立和服务器交互的方法:

<script type="text/javascript" src="viewServer.php?client"></script>

这样就可以在JavaScript中使用viewServer.php中定义的PHP对象了,因为JPSpan已经把这些代码进行的转换.

引用到的utils.js

<!-------------------------utils.js--------------------------->

<!-------------------------utils.js--------------------------->

/**

* 建立一个生成一个Form的JavaScript方法

* fmName:Form的名称,默认名称和Id相同

* title :指定一个Form的标题

* posId:指定Form放置的位置,一般设计好界面结构后的某个ID上

* actionUrl:Form的action

* methodName:Form的提交方式POST 或 GET

*/

function createForm(fmName,title,posId,actionUrl,methodName){

var htmlStr = "<form name="+fmName+" id="+fmName+" method="+methodName+" action="+actionUrl+">";

htmlStr += "<div id='table_"+fmName+"'><table border=0>";

htmlStr += "<tr>";

htmlStr += "<td style='white-space: nowrap; background-color: #CCCCCC;' align=center valign=top colspan=2>";

htmlStr += "<b>"+title+"</b>";

htmlStr += "</td>";

htmlStr += "</tr></table></div></form>";

document.getElementById(posId).innerHTML += htmlStr;

}

/**

* 为某个Form增加一个元素

* fmName:Form的名称

* elemType:元素的类型

* elemName:元素的名称

* elemLabel:元素的前面的标签

* isRequired:是否是必录项

*/

function addElement(fmName,elemType,elemName,elemLabel,isRequired){

var tbId = "table_"+fmName;

var htmlStr="<tr>";

htmlStr += "<td align='right' valign='top'>"

if(isRequired){

htmlStr += "<span style='color: #ff0000'>*</span>";

}

htmlStr += "<b>"+elemLabel+"</b></td>";

htmlStr += "<td valign='top' align='left'>";

if(elemType.toLowerCase() == 'select'){

htmlStr += "<select name="+elemName+"></select>";

}else{

htmlStr += "<input name="+elemName+" type="+elemType+"/>";

}

htmlStr += "</td></tr>";;

var tmpStr = document.getElementById(tbId).innerHTML.toLowerCase();

tmpStr = tmpStr.substring(0,tmpStr.lastIndexOf("</table>"));

document.getElementById(tbId).innerHTML = tmpStr+htmlStr+"</table>";

}

/**

* 增加一组元素,这样可以做到一组元素可以在同一行中,而不用折行

* fmName:Form的名称

* elemArr:元素的类型和名称的数组 其中名称作为键,类型为值

* elemLabel:组前面的标签

* isRequired:是否是必录项

*/

function addGroup(fmName,elemArr,elemLabel,isRequired){

var tbId = "table_"+fmName;

var htmlStr="<tr>";

htmlStr += "<td align='right' valign='top'>"

if(isRequired){

htmlStr += "<span style='color: #ff0000'>*</span>";

}

htmlStr += "<b>"+elemLabel+"</b></td>";

htmlStr += "<td valign='top' align='left'>";

for(key in elemArr){

htmlStr += "<input name="+key+" type="+elemArr[key]+" />&nbsp;";

}

htmlStr += "</td></tr>";

var tmpStr = document.getElementById(tbId).innerHTML.toLowerCase();

tmpStr = tmpStr.substring(0,tmpStr.lastIndexOf("</table>"));

document.getElementById(tbId).innerHTML = tmpStr+htmlStr+"</table>";

}

/**

* 设定元素的值

* fmName :Form名称

* elemName : 元素名称

* elemValue : 元素值

*/

function setElementValue(fmName,elemName,elemValue){

document.forms[fmName][elemName].value = elemValue;

}

/**

* 设定控件的宽度

*/

function setElementWidth(fmName,elemName,elemWidth){

document.forms[fmName][elemName].width = elemWidth;

}

/**

* 获取元素的值

*/

function getElementValue(fmName,elemName){

return document.forms[fmName][elemName].value;

}

/**

* 为下拉框设置可选的部分

* fmName :Form的名称

* elemName : 元素的名称

* optionArr :Option可选项的数组 key为值 value为名称显示

*/

function setSelectOptions(fmName,elemName,optionArr){

i=0;

for(key in optionArr){

document.forms[fmName][elemName].options[i]=new Option(optionArr[key],key);

i++;

}

document.forms[fmName][elemName].options.lengths=i;

}

/**

* 为下拉框增加一个可选项

*/

function addSelectOption(fmName,elemName,optionName,optionValue){

i = document.forms[fmName][elemName].options.lengths;

if(undefined == i){

i=0;

}

document.forms[fmName][elemName].options.lengths = i+1;

document.forms[fmName][elemName].options[i] = new Option(optionName,optionValue);

}

/**

* 为元素控件设定事件响应

* fmName :Form的名称

* elemName : 元素的名称

* eventName : 事件名称

* funcName : 处理该事件的函数名称

*/

function setElementEventHandler(fmName,elemName,eventName,funcName){

document.forms[fmName][elemName].eventName = funcName;

}

/**

* 设定元素的click事件,对上一个函数的补充

*/

function setElementClick(fmName,elemName,funcName){

//setElementEventHandler(fmName,elemName,'onclick',funcName);

document.forms[fmName][elemName].onclick = funcName;

}

/**

* 设定元素的keypress事件,对上一个函数的补充

*/

function setElementKeypress(fmName,elemName,funcName){

//setElementEventHandler(fmName,elemName,'onkeypress',funcName);

document.forms[fmName][elemName].onkeypress = funcName;

}

我们的viewServer.php是这样的

<?php

//-----------------------------------------------------------------------------------

require_once 'JPSpan/JPSpan.php';

require_once JPSPAN . 'Server/PostOffice.php';

require_once JPSPAN . 'Types.php';

//-----------------------------------------------------------------------------------

class MenuReader{

//读取代码表参数

function readcodes($cn){

$xmlParser = new codesxml();

$xmlParser->setCodename($cn);

$xmlfile = "./codes.xml";

$fd = fopen( $xmlfile, "r" );

$contents = fread($fd, filesize($xmlfile));

fclose( $fd );

$xmlParser->parse($contents);

return array($cn,$xmlParser->objs);

}

}

/**

* 读取存放代码的XML文件的解析类

*/

class codesxml{

var $parser; //解析器

var $objs; //解析后的数组

var $canParse; //是否可以开始解析

var $codeName; //代码表的名称

/**

* 构造函数

*/

function codesxml() {

$this->parser = xml_parser_create();

xml_set_object($this->parser,&$this);

xml_set_element_handler($this->parser,"tag_open","tag_close");

xml_set_character_data_handler($this->parser,"cdata");

$this->objs = array("");

$this->canParse = false;

}

/**

* 设定代码名称

*/

function setCodename($cn){

$this->codeName = $cn;

}

/**

* 解析数据

*/

function parse($data) {

xml_parse($this->parser,$data);

}

/**

* 标签开始

*/

function tag_open($parser,$tag,$attributes) {

if(strcasecmp($tag,'code')==0 && strcasecmp($attributes['NAME'],$this->codeName)==0){

$this->canParse = true;

}

if(strcasecmp($tag,"element")!=0 || !$this->canParse){

return;

}

$str = "";

foreach($attributes as $key=>$value){

$str = $str."[$key:$value]";

}

array_push($this->objs,$str);

}

function cdata($parser,$cdata) {}

function tag_close($parser,$tag) {

if(strcasecmp($tag,'code')==0 && $this->canParse){

$this->canParse = false;

}

}

}

/*-----------------------set up Listener-----------------------------------*/

$S = & new JPSpan_Server_PostOffice();

$S->addHandler(new MenuReader());

$S->addHandler(new LogReader());

//-----------------------------------------------------------------------------------

if (isset($_SERVER['QUERY_STRING']) && strcasecmp($_SERVER['QUERY_STRING'], 'client')==0) {

define('JPSPAN_INCLUDE_COMPRESS',TRUE);

$S->displayClient();

} else {

require_once JPSPAN . 'ErrorHandler.php';

$S->serve();

}

?>

好了,不多作解释,这就是JPSpan提供的框架,当然,还有另外一种方式和服务端进行交互,我们在下一篇JPSpan的深入的文章慢慢讨论.这篇文章,仅仅用一个实例来表达这种清晰分层的方法和体系结构.希望对你的认识有所帮助.对于更深入的细节以后再说.

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有