关于DWR、XMLHTTP、XML-RPC和Flex

今天看到一篇文章,介绍DWR(Direct Web Remoting),它的作用是在javascript里通过iframe直接调用Java类中的方法,可以实现像Google Suggest那种在文本框中输入时自动完成的功能(例如输入“what is the best”),很酷哦。

联想起前一阵项目中级联下拉菜单的问题,使用这种技术也是一种不错的解决方法。实现和DWR类似功能的还有XMLHTTP和XML-RPC,前者可以看作是微软对request/response的一种包装,主要用于从远程服务器取得数据;后者也是一个十分简单的协议,通过在request/response中增加xml格式的信息达到调用远程方法的目的,但需要专用的XML-RPC服务器或在现有服务器中加入对XML-RPC的支持。

要说在Web应用程序中耍酷,不得不提现在越来越流行的Macromedia的Flex。它在你现有系统结构的基础上增加一个Flex服务器,用来把xml格式的代码转换为.swf文件(也就是flash了),让你的表现层变得异常丰富,不仅外观漂亮,还能实现如拖放、渐变以及各种动画效果,用广告词来说就是“提升用户体验”。如果你看过Macromedia网站上提供的案例,相信你很难不被打动。唯一的遗憾就是Flex的价格实在太贵了:至少买双CPU的License,价格$12,000!

和商业产品相比,虽然开源的RIA可能存在一些不足,但至少在小项目中用用问题不大,Laszlo就是其中之一。

搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2004/12/15/77496.html

让IE浏览器提示下载或直接打开word文档

在很多论坛上看到这样的问题:点击一个指向.doc类型的文件后,怎样不直接在IE里打开,而是弹出一个对话框提示用户想下载还是打开。解决方法很简单,打开“我的电脑”,在菜单里选择“工具”->“文件夹选项”,在对话框里选择“文件类型”这个属性页,在列表中选中扩展名为doc的类型,按下面的“高级”按钮,在弹出的“编辑文件类型”对话框里钩上“下载后确认打开”复选框就可以了。

file

但这只是在客户机上解决了这个问题,以我的经验,在服务端不论以什么样的方式将.doc文件的流发给IE,都将由上面的设置决定是否弹出下载对话框,即使将mimetype设置为application/octet-stream也是如此。没有实验其他浏览器。

搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2004/12/15/77380.html

表单提交方式由POST改为GET出现乱码的解决

组合查询功能,原先使用<html:form>缺省是以POST方式提交的,增加了分页功能后,由于要在URL里记住用户提交的查询内容,例如:

http://localhost:8080/aims/client/filter.do?name=%E5%BC%A0&address=%E5%8C%97%E4%BA%AC&title=&duty=&departmentCode=10000001&categoryCode=10000002&fieldCode=10000006&genderCode=&identityCode](http://localhost:8080/aims/client/filter.do?name=%E5%BC%A0&address=%E5%8C%97%E4%BA%AC&title=&duty=&departmentCode=10000001&categoryCode=10000002&fieldCode=10000006&genderCode=&identityCode)=

所以表单的提交方式要改为GET。

只是简单的改为method="GET",但这样一改却让action无法得到正确的输入值,例如用户在姓名条件里输入,在action里用theForm.getName()会得到形如%A4的乱码,不仅查询结果是错误的,而且在重新显示的查询表单的姓名栏里也显示出乱码。

我试了很多种转码也没转成原来的值,问了很多朋友,最后的解决方式还是通过转换编码,是把ISO8859-1转为UTF-8,即:

String name=new String(theForm.getName().getBytes("ISO8859-1"),"UTF-8");

注意我的应用程序里已使用了encoding为UTF-8的Filter。

虽然要加手工转码的代码很不爽,但只在这一处而已,也不碍大事。只是我现在的环境是Tomcat+Mysql,不知道换到其他服务器上会不会重新出现乱码问题,好在这个项目不需要考虑这个问题。

据说Tomcat处理POST和GET的请求时处理编码的方式不太一样,我还看到有篇帖子说要在server.xml<Connector>里加URIEncoding="GBK"属性,但我试了不起丝毫作用。

搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2004/12/14/77129.html

Web应用程序从Tomcat移植到WAS

为了方便起见,示范中心项目一直在Tomcat 4.1+Mysql 4.0的环境下开发。现在客户提出运行环境将是WAS 5.1+DB2 8.0,在移植的过程中发现现有的程序存在两个问题。

1、在Tomcat下类似edit.do?service这样的url,使用request.getParameter("service")可以得到非空值,但在WAS下则得到空值,必须使用edit.do?service=1这样的完整形式。

2、原先有一些不太规则的标签写法,比如下面这个:

<bean:define id="toDel" name="client" property="name" type="String"/>
<html:link action="/delete" paramId="code" paramName="client" paramProperty="code" onclick="<%="return confirmDelete('"+toDel+"');"%>"> 
    <html:img page="/../images/btn_del.gif" width="41" height="16" border="0"/>
</html:link>

在Tomcat里是正常的——当用户点击“删除”时提示“是否确认删除XXX?”,但在WAS里含有这个代码的页面都会无法通过编译,只能把提示内容后面的XXX去掉。我觉得struts内置的标签库对字符串的操作实在有限。

除去这两点,暂时没有发现其他不兼容之处。想不明白的是,既然Tomcat是Servlet 2.3的参考实现,就应该是最“标准”的,为何还会出现这种情况,是Tomcat有自己的扩展,还是WAS的实现有不足呢?

搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2004/12/12/76093.html

Hibernate用Mysql数据库时链接关闭异常的解决

在一个项目中,客户要求除操作系统外全部使用免费软件,因此我使用了Mysql 4.0作为数据库服务器,其JDBC驱动为3.0.9版本,在给客户安装后调试一切正常。可是到了第二天,只要一登录就提示“No operations allowed after connection closed”异常,显示在浏览器上。在经过一番检查后我发现,在这种情况下只要重新启动Tomcat就恢复正常,然而到了第二天问题依旧。

在网上查找一下,原来Mysql在经过8小时不使用后会自动关闭已打开的连接,摘录原文如下:

I have a servlet/application that works fine for a day, and then stops working overnight

MySQL closes connections after 8 hours of inactivity. You either need to use a connection pool that handles stale connections or use the "autoReconnect" parameter (see "Developing Applications with MySQL Connector/J").

Also, you should be catching SQLExceptions in your application and dealing with them, rather than propagating them all the way until your application exits, this is just good programming practice. MySQL Connector/J will set the SQLState (see java.sql.SQLException.getSQLState() in your APIDOCS) to "08S01" when it encounters network-connectivity issues during the processing of a query. Your application code should then attempt to re-connect to MySQL at this point.

在客户那边,晚上时间是不会有人使用这个系统的,就造成了系统中原先没有考虑到的这个情况。

为此我试验了三种方法:

1、在数据库的url中加入&autoReconnect=true

2、在每次调用getSession()方法时判断session.isClosed()是否为真,若为真则调用session.reconnect();

3、在经过两天,事实证明前两种方法都不起作用的情况下,我在这个页面找到了第三种方法,即不使用Hibernate内置的连接池(Hibernate强烈推荐不使用但我以前一直在用),改用C3P0连接池,这个连接池会自动处理数据库连接被关闭的情况。要使用C3P0很简单,先从Hibernate里把c3p0-0.8.3.jar复制到项目的lib目录中,再在hibernate.properties里去掉hibernate.c3p0开头的那些属性的注释(使用缺省值或自己需要的数值),这样Hibernate就会自动使用C3P0代替内置的连接池了。到目前为止前面的问题没有再出现过。

以前对Hibernate警告不要使用内置连接池作产品用途没有太放在心上,这次是一个教训,所以不论从稳定还是性能的考虑,都应该选择相对更加成熟的连接池。

Update: 除了连接池的原因,原先写的HibernateDAO类也有问题,在有些情况下一个session会被多个请求反复使用,现在已改正。另外,c3p0这个名字不是星球大战里那个机器人么?

搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2004/12/10/75145.html

Encoded vs Literal, RPC vs Document

我一直没有分清题目里所写的概念,看过JAX-RPC规范后还是模糊,原因主要是对XML本身就没有特别深入的理解。不过现在感觉好象明白了一些。

Encoded和Literal是两种Typing system,前者对应http://schemas.xmlsoap.org/soap/encoding/,后者利用XML Schema定义数据类型。

“Literal对应于types中的element只有一层,Encoded对应于types中element多层的。

用Literal描述的参数客户必须提供明确的参数名,用Encoded描述的参数客户可以用param1,param2,param3等代替。”

在Axis中,使用org.apache.axis.description.OperationDesc类指定自己要使用的方式,方法如下:

“oper.setStyle(org.apache.axis.enum.Style.DOCUMENT); oper.setUse(org.apache.axis.enum.Use.LITERAL);,这个是对于document方式的,如果是rpc方式应该设置为:oper.setStyle(org.apache.axis.enum.Style.RPC); oper.setUse(org.apache.axis.enum.Use.ENCODE);”,然后还要用call.setOperation(oper);把OperationDesc对象实例和Call对象联系起来。

----赵晓冬

关于RPC和Document的比较,这里有两篇不错的文章:Operation Style (Document/RPC) and Message format(literal/encoded)The Difference Between RPC and Document Style WSDL

搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2004/12/06/73517.html

Struts分页的一个实现

在Web应用程序里,分页总让我们开发人员感到很头疼,倒不是因为技术上有多么困难,只是本来和业务没有太多关系的这么一个问题,你却得花不少功夫来处理。要是稍不留神,时不时出点问题就更郁闷了。我现在做的一个项目也到了该处理分页的时候了,感觉以前处理得都不好,所以这次有所改变,基本目标是在现有(未分页)的代码基础上,尽量少做修改,并且同样的代码可以应用于不同模块的分页。以下就是我用的方法:

首先,考虑分页绝大多数发生在列表时,组合查询时也需要用到。在我的项目里,列表的Action一般名字为ListXXXActioin,例如客户列表是ListClientsAction等等。在未分页前,ListXXXAction里会把所有的对象取出,通过request.setAttribute()放在request里,然后将请求转向到列表的jsp(例如listClients.jsp)显示出来(你可能会说不要在Action里放业务逻辑,但现在这不是我们考虑的重点)。而分页后,我们只取用户请求页对应的那些对象。为了最大限度的达到代码重用,我做了以下工作:

1、新建一个Pager类,该类有beginPage、endPage、currentPage、pageSize和total等int类型的属性,分别代表开始页、结束页、当前页、每页记录数和总记录数,它主要是让jsp页面显示页导航使用的。请注意currentPage属性是从0开始的。

2、新建一个AbstractListActioin类,并让所有ListXXXAction都继承它。在这个类里覆盖execute()方法,可以在这里判断权限等等,并在判断权限通过后执行一个abstract的act()方法,这个act()由ListXXXAction来实现。

3、在AbstractListAction里增加getPage()方法,用来从request得到用户请求的页码(若未请求则认为是第0页):

protected int getPage(HttpServletRequest request) { 
    String p = request.getParameter("p"); 
    if (p == null) 
        return 0; 
    else 
        try { 
            return Integer.parseInt(p); 
        } catch (NumberFormatException e) { 
            return 0; 
        } 
} 

4、在AbstractListAction里增加makePager()方法,用来向request里增加一个Pager类的实例,供jsp页面显示页导航:

protected Pager makePager(HttpServletRequest request, int total) { 
    Pager pager=new Pager(); 
    pager.setTotal(total); 
    pager.setPageSize(Config.getInstance().getPageSize()); 
    pager.setBeginPage(0); 
    pager.setEndPage(((pager.getTotal()) - 1) / pager.getPageSize() + 1); 
    pager.setCurrentPage(getPage(request)); 
    return pager; 
}  

注意在我的项目里,每页记录数是写在配置文件里的,如果你没有配置文件,上面第4行setPageSize()的参数直接填数字即可,例如pager.setPageSize(10);

5、这样,所有的ListXXXAction都可以使用getPage()得到请求的页码,并且能够方便的通过makePager()构造需要放在request里的pager对象了。现在要在从数据库取数据的代码上再做一些修改,即只取所需要的那一部分数据。由于我的项目中使用了Hibernate,所以这个修改也不是很困难。未分页前,在我的ListClientsAction里是通过构造一个Query来得到全部Client的,现在,只要在构造这个Query后再加两句(setMaxResults和setFirstResult)即可:

Query query =  ;//构造query的语句  
int total =  ;//得到总记录数  
Pager pager = makePager(request, total);//调用父类中的方法构造一个Pager实例 
query.setMaxResults(pager.getPageSize());//设置每页记录数 
query.setFirstResult(pager.getCurrentPage() * pager.getPageSize()); //设置开始位置 
request.setAttribute(Pager.class.getName(), pager);//把pager放在request里 
request.setAttribute(Client.class.getName(), query.list());

目前存在一个问题,就是在上面代码的第二句中,应该是获得总记录数,但我暂时没有特别好的办法不得到全部对象而直接得到记录数,只能很恐怖的用“int total = query.list().size();”,汗……

6、最后,我写了一个页导航的jsp页面pager.jsp,供各个显示列表的jsp来include,代码如下:

<%Pager pager=(Pager)request.getAttribute(Pager.class.getName());%>
<table width="90%" border="0" align="center" cellpadding="2" cellspacing="1" bgcolor="#CCCCCC">
<tr>
    <td bgcolor="#EEEEEE" align="right">
    <bean:message key="prompt.pager" arg0="<%=String.valueOf(pager.getTotal())%>"/>
        [
<%
for(int i=pager.getBeginPage();i<=pager.getEndPage();i++){
    if(i==pager.getCurrentPage()){
    %>
        <%=(i+1)%>
    <%}else{
        String qs=request.getQueryString()==null?"":request.getQueryString();
        String op = "p="+pager.getCurrentPage();//Original page parameter expression
        String np = "p="+i;//New expression
        if(qs.indexOf(op)==-1)
            qs=np+"&"+qs;
        qs=qs.replaceAll(op,np);
        %>
        <a href="<%="?"+qs%>"><%=(i+1)%></a>
    <%}%>
    <%if(i<pager.getEndPage()-1){%>
     
    <%}%>
<%}%>
]
</td></tr>
</table>

我觉得有必要解释一下,在上面的代码中,关于每一页对应的url是这样处理。request.getQueryString()中可能包含“q=2”这样的页码请求,也可能不包含即缺省请求第0页,所以统一用replaceAll()方法将其去掉,然后将对应的页码请求串(如“q=3”)加在qs的前面。这样做的好处是,每个模块都可以使用这个页导航,并且不会丢失url中的其他参数(例如今后加入排序功能后,url中可能包含“direction=desc”这样的参数)。

05-4-14 Update:我发现在Tomcat4.1和Websphere5.0里,request.getRequestURL()方法得到的地址是不一样的,所以考虑到兼容性,每个页码的链接都使用相对本页的链接。

在列表jsp(listClients.jsp)中,很简单的这样include它(之所以要放在里,是希望在没有记录可显示的时候就不显示页导航了):

<logic:notEmpty name="<%=Client.class.getName()%>"> 
    <%@include file="/pager.jsp"%> 
</logic:notEmpty>

经过上面几步的处理,我的客户列表已经可以实现分页了,效果见下图。如果在另外一个模块中也需要分页,比如部门列表时,只需要1、修改ListDeptsAction继承AbstractListAction,2、在ListDeptsAction里增加setMaxResults()和setFirstResults()方法,3、在listDepts.jsp中适当的位置include页导航,就可以了,改动是相当小的。

file

最后,如果希望组合查询的结果也能够分页,必须指定组合查询表单的method属性为“GET”,这样查询要求会被记录在url中,分页导航从而能够正常的工作(每次换页都将查询要求和请求的页码提交)。

搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2004/12/06/73417.html

多模块Struts应用程序的几个问题(及部分解决方法)

Struts从1.1版本开始支持把应用程序分为多个模块,每个模块可以看作独立的应用程序,在带来方便的同时,我也发现了一些问题。比如有一个struts应用程序分了大约十个模块,现在有以下问题不知道大家一般是怎么解决的:

1、因为要进行验证,所以在每个模块对应的资源文件里都要有“errors.required={0} is required.”等资源,有没有只用在一个文件里定义的方法?

2、用tiles的时候,要在每个模块对应的tiles-defs.xml里定义几乎相同的definition,有没有只用在一个文件里定义的方法?(我试过在缺省模块里定义一个definition,然后在模块里extends它,但不行,extends似乎只找当前模块)

3、使用ExceptionHandler的时候,为什么在exception标签里指定了bundle属性还是只在当前模块里找资源?我希望把一些重复使用的异常处理声明在一个文件里,例如NotLoginException、NoSuchObjectException等等,并且它们对应的key也指向同一个资源文件里的资源(利用bundle属性),怎么实现?

经过一段时间的摸索,第一个和第三个问题基本上解决了,其实它们可以看作同一类问题,就是资源的问题。在struts-config-xxx.xml里定义资源文件时,可以指定一个factory属性,不指定时使用缺省的“org.apache.struts.util.PropertyMessageResourcesFactory”类。我的解决方法是自定义一个CustomMessageResourcesFactory类,将多个资源文件以逗号分隔的形式作为参数(即message-resources的parameter属性)传给它,在需要资源的地方会遍历它们进行查找。同时还要自定义一个CustomMessageResources类,它的getMessage()方法里是查找资源的关键代码,而factory只是解析逗号分隔的参数构造并返回CustomMessageResources实例。

CustomMessageResourcesFactory的代码比较简单,如下所示:

package com.acme;

import java.util.Arrays;

import org.apache.struts.util.MessageResources;
import org.apache.struts.util.MessageResourcesFactory;

public class CustomMessageResourcesFactory extends MessageResourcesFactory{

    public MessageResources createResources(String config) {

        return new CustomMessageResources(Arrays.asList(config.split(",")));
    }

}

CustomMessageResources就稍微复杂一些,不过很幸运,我在网上找到了一个完全符合自己要求的类,下载地址在这里,如果链接已失效请联系我。

这样,在每个模块的struts-config-xxx.xml里,只要像下面这样定义资源文件就可以实现共享资源的功能了,其中ErrorResources中是所有模块都需要的错误信息资源:

<message-resources factory="eg.CustomMessageResourcesFactory" 
    parameter="eg.ApplicationResources,eg.ErrorResources" />

上面参考了这篇文章http://javaboutique.internet.com/tutorials/Dynaform/index-7.html,它是通过修改ActionServlet使用CustomMessageResources的,我觉得还是自定义factory的方式更自然些。

第二个问题暂时还没有解决,也许要修改handler实现。

搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2004/11/23/67425.html

Web页中级联下拉选择框问题的解决方法

示范中心项目里有一些页面要求几个下拉选择框的内容是具有关联的关系的,例如在编辑一个实验项目时,要先在一个下拉框里选择该项目所在的示范中心,这时实验室下拉框里的内容要根据用户选中的示范中心改变。为了实现这个目的,我们先后想了几种方法。

1、在用户选择示范中心时,刷新页面,并把示范中心代码加在url的后面传给action。这样的最大问题是,如果用户已经填写了一部分表单,在更改示范中心选择的时候会丢失已填写的信息。除非在刷新时把整个表单的所有域都加在url里传回,但这样做要在jsp页面里增加非常多的逻辑,因此不能使用。

2、把与示范中心关联的几个下拉框作为iframe的方式,当选择了一个示范中心时,根据所选的示范中心重新load这个iframe,这样不会影响到其他可能已经填写的域。这样做的问题是,iframe对应的页面里要重复考虑权限问题,这是不能忍受的,同时和所有使用框架技术的应用一样,有可能造成一些页面不一致的情形。

3、前面两种方式虽然都被否决了,但它们共同的好处是在用户改变对示范中心的选择时,数据是动态加载的。第三种方式是把所有数据一次读到客户端,使用javascript的方法实现关联。我在网上找到了一些js代码,但大部分都不适合动态生成数据,这也是一开始没有使用js方式的原因之一。不过最后还是找到了一个我认为不错的js,对它进行少量修改后比较漂亮的完成了任务,下面就说说具体的方法。

这个js的演示和下载地址在http://fason.nease.net/samples/select/,这里我要感谢这位fason朋友提供这么好的工具。在需要关联的页面里,例如编辑实验项目的页面,首先要引入xselect.js这个script,然后构造一个Dsy对象,使用这个对象的add方法增加关联下拉框的各个选项。还有两个数组,sel是关联下拉框的id数组,def是这些下拉框缺省的选项值数组。最后使用attachSelect函数将dsy对象和这两个数组关联起来就大功告成。对于选项是动态生成的情况,就是要动态生成这么一段javascript,如下所示:

<script language="JavaScript" src="../xselect.js"></script>
<script language="JavaScript">
<!--
var dsy = new Dsy();
var sel = ["demoCenter","lab"];
var def = ['<bean:write name="projectForm" property="demoCenterCode"/>','<bean:write name="projectForm" property="labCode"/>'];

dsy.add("0",[['<bean:message key="select.prompt" bundle="root"/>','']
<logic:iterate name="<%=DemoCenter.class.getName()%>" id="dc">
    ,['<bean:write name="dc" property="name"/>','<bean:write name="dc" property="code"/>']
</logic:iterate>
]);

dsy.add("0_0",[['<bean:message key="select.prompt" bundle="root"/>','']]);
<logic:iterate name="<%=DemoCenter.class.getName()%>" id="dc" indexId="index">
    dsy.add('0_<%=index.intValue()+1%>',[['<bean:message key="select.prompt" bundle="root"/>','']
    <bean:define id="code" name="dc" property="code" type="String" />
    <logic:iterate name="<%=Lab.class.getName()%>" id="lab">
        <logic:equal name="lab" property="demoCenter.code" value="<%=code%>">
            ,['<bean:write name="lab" property="name"/>','<bean:write name="lab" property="code"/>']
        </logic:equal>
    </logic:iterate>
    ]);
</logic:iterate>
attachSelect(dsy,sel,def);
//-->
</script>

生成的静态页面就是下面这样,页面显示后会缺省选择“北京大学数学示范中心”和“软件工程实验室”:

<script language="JavaScript" src="../xselect.js"></script>
<script language="JavaScript">
<!--
var dsy = new Dsy();
var sel = ["demoCenter","lab"];
var def = ['10000001','10000007'];

dsy.add("0",[['--------请选择--------','']
    ,['教育部','10000000']
    ,['北京大学数学示范中心','10000001']
    ,['北京师范大学普通话示范中心','10000002']
    ,['北京邮电大学卫星通信示范中心','10000003']]);
dsy.add("0_0",[['--------请选择--------','']]);
dsy.add('0_1',[['--------请选择--------','']
        ,['生物实验室','10000004']
        ,['软件工程实验室','10000007']
]);
dsy.add('0_2',[['--------请选择--------','']
        ,['数据挖掘实验室','10000002']
        ,['微电子实验室','10000003']
        ,['Web服务实验室','10000006']
]);
dsy.add('0_3',[['--------请选择--------','']
        ,['航空航天实验室','10000001']
        ,['湍流实验室','10000005']
        ,['互联网实验室','10000008']
]);
dsy.add('0_4',[['--------请选择--------','']
]);    
attachSelect(dsy,sel,def);
//-->
</script>

不过这个js有个小问题,就是它的def数组中的每个元素只能是一个字符串,当选择框是可多选的列表框时,就不能实现多个选项的预选择(populate)。为了解决这个问题,我对xselect.js进行了少量的修改,允许def中的每个元素是一个数组。以下是修改后的xselect.js文件中的doChange()函数:

function doChange(v) {
    var str = "0";
    for (var i = 0; i < v; i++) { str += ("_" + Sel[i].selectedIndex); };
    var ss = Sel[v];
    if (oDsy.Exists(str)) {
        with (ss) {
            length = 0;
            var ar = oDsy.Items[str], xx = 0;
            for (var i = 0; i < ar.length; i++) {
                var ot = ar[i][0], ov = ar[i][1] ? ar[i][1] : ot;
                //if (ov == Store[v]) xx = i;
                options[i] = new Option(ot, ov);

                //Added by zhanghao
                if(Store[v] instanceof Array){
                    for(var j=0;j<Store[v].length;j++)
                        if(Store[v][j] == ov)options[i].selected = true;
                }else{
                    if(ov == Store[v])options[i].selected = true;
                }
            }
            //options[xx].selected = true;
            if (++v < Sel.length) doChange(v);
        } 
    } else {
        for (var i = v; i<Sel.length; i++) {
            with (Sel[i]) {
                length = 0;
                options[0] = new Option("--", "");
                options[0].selected = true;
            }
        }
    }
}

其中注释掉了两句,并增加了一段对数组元素的处理,目前这个js在示范中心系统里工作良好。为了让jsp页面中的代码进一步减少,我们今后还要把动态生成js的代码写为tag的方式。

Web页中级联下拉选择框问题是一个非常普遍的问题,本贴里介绍的是我认为十分圆满的一个客户端方式的解决方法,用户在使用时不会感到任何不便,而且因为数据权限问题是在action里已经解决的,在对应的jsp页面里所做的任何处理都不需要考虑数据权限了。但在一些数据量特别大的系统里要考虑网络连接速度是否适合将所有数据传到客户端的问题。

Update: 相关代码下载

搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2004/11/15/64006.html

在Win2000里快速切换TCP/IP设置

自己的笔记本要在多个地方上网,但家里是自动获取IP的,实验室有自己的子网,宿舍又有另一个子网,每次换地方都得手动换TCP/IP设置,很麻烦。今天在网上看到一篇文章,原来要解决这个问题是非常简单的,不需要安装任何软件,下面是我的做法。

首先,在网络属性里把TCP/IP配置为实验室用。然后,运行cmd打开命令行窗口,输入如下命令:

netsh -c interface dump > lab.txt

这样就在C:\Documents and Settings\Administrator下得到一个lab.txt文件,当然你也可以在上面的命令里使用绝对路径改变它的存放位置。

接下来,把TCP/IP分别设为其他各处的正确配置,并使用同样命令生成不同的文件,对我来说就是home.txtdorm.txt了。

需要改变TCP/IP设置的时候,例如我要切换到家里上网了,只要执行下面的命令:

netsh -f home.txt

切换就完成了,很方便吧。总结一下就是两条:netsh dump导出设置,netsh -f导入设置。

还要提醒一点,如果是切换到自动获得IP的方式,最好用ipconfig /renew命令强制获得一下,否则要等一段时间才能上网。

搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2004/11/04/60551.html