上上礼拜五台湾来的客人做演示的时候,系统突然出现了问题(非常糗).页面参数传递出现的问题,也导致了页面验证的失败。其实本来一切都OK的。在这之前自己也做过很多测试,YC师兄之前也给我大略测试了一下,也提了一些建议(^_^)。
其实后来想想,出现问题我倒不怕,咱搞技术的,说白了就是跟问题打叫道。问题来了,我把你摆平,问题不来,我硬是给你搞出来,然后再把你摆平.
后来分析了下问题,发现罪魁祸首是前人写好的验证方法有问题。
故事背景:
一个表单录入界面,包含记录时间字段,开始透析时间字段,转归时间字段,心率字段,备注字段,需要完成的验证过程包括:
1记录时间不能在当前时间之后;
2开始透析时间不能在当前时间之后;
3转归时间不能在当前时间之后;
4转归时间不能在开始透析时间之后;
5心率输入内容必须为double型,不能输入非数字内容
6备注内容长度必须小于等于某一个值;
7 记录时间,记录人是必填项
前人采用的验证方法是通过form.get**把表单内容传递到action里面,然后在action里面调用验证方法进行验证。下面是原来的验证方法:
/** */ /**
* 验证必填项
* @param exp
* @param value
* 验证参数
* @param arg
* 显示参数1
* @return
*/
public static boolean vldRequired(BaseException exp, String value,
String arg) ... {
//省略
}
/** */ /**
* 验证最小长度
*
* @param exp
* @param value
* @param len
* @param arg
* @return
*/
public static boolean vldMinLength(BaseException exp, String value,
int len, String arg) ... {
//省略
}
/** */ /**
* 验证输入内容是否为数字
*
* @param exp
* @param value
* @param arg
* @return
*/
public static boolean vldNumber(BaseException exp, String value, String arg) ... {
//省略
}
在上面的方法中,一旦检测到问题,马上通过addErrMsg添加错误信息,再统一把异常信息发布出去。初步看来没有什么问题,实际上有问题。
比如说,public static boolean vldNumber(BaseException exp, String value, String arg),他是对字符串value进行检测,看看字符串的组成内容是否全部为数字。实际上在我们定义数据表字段的时候,牵涉到数字的字段,一般都定义为NUMBER型,在JO中对应为BigDecimal,在域模型中对应为Double,在formbean中对应为Double。因此通过form.get**取得的内容应该为Double。其实这都没什么,可以通过tostring方法转化为Sting类型。但是,我发现,对于我的例子来说,定义为Double型的心率,一旦输入非数字内容,比如说aw,或者12w等等,通过form.getHeartRate()取得的内容就是NULL!既然取得NULL,又怎么可以运用toString()方法呢?一定会抛出NullPointException的!
郁闷了一天,实在找不到方法解决这个瓶颈问题,不得不采用其他方法来解决。首当其冲我就想到运用Struts ValidateForm。查了一天资料,学习了怎么配置,正准备放手干的时候,发现系统里已经有了ValidateForm的身影。配置也完成了,只是却没有用上它进行表单验证。于是马上联系之前负责这个系统开发的CY师兄,原来前人也打算用他进行验证,后来发现不灵活,放弃了。既然我所崇拜的牛人都绕道而行,我也马上放弃,另辟蹊径。
在CSDN溜达了一会,再跟CY师兄讨论了一会,他说,那就采用javascript直接在页面进行验证吧!
简直就是一语惊醒梦中人啊!但是,可怜我对javascript一窍不通,我更倾向于喜欢业务层,数据层的工作。(因为我觉得我美工好差)
也没办法,边学边用!马上下载了一本javascript速查手册!
首先,修改form代码:
<html:form action="/ClinicOutcome.do?operate=save" οnsubmit="return validateClinicOutcomeForm();">
表单提交后,马上调用 validateClinicOutcomeForm()验证方法。
接下来,传值!怎么把form里的内容传进来呢?
方法一:oElement = document.getElementsByName(sIDValue),把ID值给了他,还是不行。说明文档里就这样说的:Retrieves a collection of objects based on the value of the NAME attribute.
我的代码:
Var heartRate=Document.getElementsByName(“heartRate”);
其属性length=1,说明里面应该是有内容的,但是,通过heartRate[0]却取不出来!奇怪,谁懂,告诉我!
方法二:再查手册,得到集合:doucument.all
我的代码:doucument.all.heartRate.value,轻松得到字段内容! O(∩_∩)o
得到内容之后就是验证。验证感觉没什么,验证数字,用正则表达式,验证长度用length 属性,验证必填项目也可以用length,验证时间呢?有点小插曲,虽然现在想想很简单,但是我也走了一段弯路。其中曲折省略,贴出代码吧。因为发现网上好多好多人因为Date这个问题犯糊涂(google过就知道,问题实在太多了)。
/**/ /*
* return s
*/
function GetCurrentDate()
... {
var d, s="" ;
d = new Date(); //自动得到当前时间。
s += d.getYear()+"-";
s += (d.getMonth() + 1) + "-";
s += d.getDate();
return(s);
}
// /比较两个时间孰小孰大
/**/ /*
* parm time1,time2,var型
* parm a,b,用于提示
*/
function comparetime(time1,time2,a,b) ... {
var tmptime1,tmptime2;
tmptime1 = new Date(time1.split("-")[0],time1.split("-")[1] - 1,time1.split("-")[2]);
tmptime2 = new Date(time2.split("-")[0],time2.split("-")[1] - 1,time2.split("-")[2]);
if(Math.floor((tmptime2.getTime() - tmptime1.getTime()) / 1000 / 60 / 60 / 24)>=0)
...{
//document.write.Submit.disabled = true;
return true;
}
else
...{
document.writeln("<font color=red size=4> ■ "+ a +"在"+b+"之后,无效! </font> <br> ");
return false;
}
}
思路很简单,首先分别把两个时间从字符型解析成Date型,通过gettime分别得到他们与基准时间的差(毫秒级别),再做差转换,搞定!
验证出问题,该怎么提示?这个问题很头疼,我觉得我到现在还找不到很好的方法来解决。
一般都用window.alert(***)来给出提示信息,但是,加入错误多呢?发生10个错误呢?难道跳出10个警告窗口?不行!
能不能在一个窗口中显示10条警告信息呢?好想这么做。但是我没有做出来。原因如下:
要想把10条警告信息一起给出来,那么必须在所有验证完成后才抛出警告窗口,那我该怎么把警告内容从检验函数里面传递出来呢?检测函数必须返回True or False.用C++里面的引用?javascript里面没有引用。
要是有朋友知道这个功能怎么实现,记得一定要告诉我。
退而求次之,我采用document.write(***)直接在页面上打印错误信息。有个地方不懂,我用了document.writeln()居然无法换行,跟采用document.write()效果是一样的,到现在我也还不懂。所以我用了<br>强行换行,哈哈。
输入错误信息之后,该怎么返回原来的输入界面呢?难道要用户按BackSpace?我想到这么一段代码:
<p class="pushButton"><a href="javascript:window.history.back()" target="_self" class="pushButton" title="返回" > 返回 </a></p>
当有错误信息的时候,“返回”出现,当没有错误信息的时候,“返回”消失。
一开始我是这么实现的:
... {
document.writeln("<td class="pushButton"+ nowrap>");
document.writeln("<p class="pushButton">");
document.writeln("<a href="javascript:window.history.back()" target="_self" class="pushButton" title="返回" > 返回 </a></p>");
document.writeln("</td> ");
return false;
}
发现document.all.length很奇怪,有时候得到的数字是8,有时候是6,有时候则是324,266.而页面内容很简单,就大约10来字的一句提示信息。再次改道,我增加了一个标志var flag=0;一旦给出错误信息,flag置1.再通过检测flag的值,决定“返回”的出现与否。
下面详细给出validateClinicOutcomeForm的代码。
... {
var fillinDate= document.all.fillinDate.value;
var fillinDoctor=document.all.fillinDoctor.value;
var outcomeReason=document.all.outcomeReason.value;
var dialyseStartDate=document.all.dialyseStartDate.value;
var outcomeDate=document.all.outcomeDate.value;
var dialyseResumeDate=document.all.dialyseResumeDate.value;
var dialyseResumeReason=document.all.dialyseResumeReason.value;
var flag= 0 ; // the flag to identify whether there is any errors throw out.
//必填项检验
if(vldRequired(fillinDate,"填表日期")!=true)
...{
if(flag!=1)
...{flag=1;}
}
if(fillinDoctor=="-请选择-")
...{
document.writeln("<font color=red size=4> ■ "+"填表医生为必填项 </font> <br> " );
if(flag!=1)
...{flag=1;}
}
//没有数字检验
//字符长度检验 如果没有内容,则不检验
if(outcomeReason.length>0) //is not null
...{
if(CheckLength(outcomeReason,40, "转归原因")!=true )
...{
if(flag!=1)
...{flag=1;}
}
}
if(dialyseResumeReason.length>0) //is not null
...{
if(CheckLength(dialyseResumeReason,40, "转归原因")!=true )
...{
if(flag!=1)
...{flag=1;}
}
}
//时间先后检验
if(fillinDate.length>0)
...{
if(comparetime(fillinDate,GetCurrentDate(),"填表时间","当前日期")!=true)
...{
if(flag!=1)
...{flag=1;}
}
}
if(dialyseStartDate.length > 0)
...{
if(comparetime(dialyseStartDate,GetCurrentDate(),"开始透析日期","当前日期")!=true)
...{
if(flag!=1)
...{flag=1;}
}
}
if(outcomeDate.length > 0)
...{
if(comparetime(outcomeDate,GetCurrentDate(),"转归日期","当前日期")!=true)
...{
if(flag!=1)
...{flag=1;}
}
}
if(dialyseResumeDate.length > 0)
...{
if(comparetime(dialyseResumeDate,GetCurrentDate(),"恢复透析日期","当前日期")!=true)
...{
if(flag!=1)
...{flag=1;}
}
}
if(dialyseStartDate.length > 0 && outcomeDate.length > 0)
...{
if(comparetime(dialyseStartDate,outcomeDate,"开始透析日期","转归日期")!=true)
...{
if(flag!=1)
...{flag=1;}
}
}
if(outcomeDate.length > 0 && dialyseResumeDate.length > 0)
...{
if(comparetime(outcomeDate,dialyseResumeDate,"转归日期","恢复透析日期")!=true)
...{
if(flag!=1)
...{flag=1;}
}
}
if(flag==1)
...{
document.writeln("<td class="pushButton"+ nowrap>");
document.writeln("<p class="pushButton">");
document.writeln("<a href="javascript:window.history.back()" target="_self" class="pushButton" title="返回" > 返回 </a></p>");
document.writeln("</td> ");
return false;
}
else
return true; //这句很重要
}
具体的验证方法我打包成js包,以后当需要用到的时候,只要加上这么一句就可以了。
charset="GBK"是用来解决显示中文乱码问题的!这个小问题很多人也碰到哦。其实不用设置什么什么,就加这句就可以了。
感觉这个礼拜有点郁闷,进展很慢。加油!