复合组件之 打分组件
2009-06-16 由 赵晓慧 发表 有3076人浏览
新网站上线之后,很多用户评论说示例中的打分组件很好用,问AOM有没有这样的组件,我们做了这样一个打分的复合组件,用户可以根据自己的需要在系统中引用这个组件进行打分操作。
AOM为我们提供了原生Ajax支持的JSF引擎,但是用户同样可以自己定义AJAX实现,本文就会用到dwr(基于AJAX实现的在javascript中直接调用java方法),所以用户以前的知识积累在AOM同样可用。
上图为打分组件的预期实现,点亮的星星显示当前的评分,附有详细的文字说明已有几人评分和当前评价分值,用户可以通过点击星星图标来评分,每颗星星代表不同的分数值(分值从小到大),鼠标移到星星上时会出现提示信息,如第一颗星星是“1分”,第五颗星星是“5分”等。
组件预期为 <my:score titles="1|1分,2|2分,3|3分,4|4分,5|5分" isAccess="true" currentScore="#{score_dwr.currentScore}" scoreCount="#{score_dwr.scoreCount}" jsName="score_dwr" methodName="score"/>
组件共提供六个属性,titles为每颗星星的分值和提示信息,用户可根据需求自行设定,但格式必须是"分值|提示信息",每组信息之间用'',"隔开,而且分值应该是等差递增的一组数据,默认值为"1|1分,2|2分,3|3分,4|4分,5|5分";isAccess用来判断用户是否有打分权限,可根据自身系统的登录用户的权限给isAccess赋boolean类型的值,同样也可以用来限制每个IP地址只能进行一次评分等,默认值为true;currentScore是当前的用户评价分值,在每次进入该页面时显示,当前无人评分时默认值为0;scoreCount是当前已评分人的个数,当前无人评分时默认值为0;jsName是在js中用到的java类的名字,在dwr.xml中自定义;methodName是js调用的java类中的方法名。其中titles,isAccess,currentScore,scoreCount是选填属性,有默认值;jsName,methodName属性是必填的。
组件主要采用operaMasks的模板技术结合js代码实现,用dwr实现在javascript中直接调用java方法。
首先按照制作复合组件的流程(具体请参考 《使用OperaMasks Studio创建Facelet复合组件》)来定义打分组件,score.xhtml,在其中根据用户定义的titles="1|1分,2|2分,3|3分"属性信息生成相应个数的星星图标,在每个星星图标上定义事件(调用js中的函数实现)onmouseover:当鼠标移到第n颗星星图标上时前n颗星星点亮;onmouseout:鼠标移开时星星图标恢复成显示当前的用户评价分值;onclick:点击星星图标实现打分操作,因为打分组件一般都嵌套在系统中作为页面的一小部分使用,而每次打分操作之后刷新整个页面消耗太大,所以我们的做法是用dwr实现在js函数中直接调用java类方法,根据方法的返回值刷新打分组件的这部分数据,java类方法和dwr的相关配置要用户自己实现。
下面我们就来介绍详细的代码实现。
score.xhtml核心代码
<ui:composition xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:c="http://java.sun.com/jstl/core" xmlns:h="http://java.sun.com/jsf/html"
xmlns:om="http://www.apusic.com/jsf/misc">
<script language="JavaScript" src="js/submit.js" type="text/javascript" />
<script type='text/javascript' src='dwr/util.js'></script>
<script type='text/javascript' src='dwr/engine.js'></script>
<script type='text/javascript' src='dwr/interface/#{jsName}.js'> </script>
<om:elite>
<![CDATA[
if(!defined(isAccess)) {
define isAccess = true;
}
if(!defined(currentScore)) {
define currentScore = 0;
}
if(!defined(scoreCount)) {
define scoreCount = 0;
}
if(!defined(titles)) {
define titles = '1|1分,2|2分,3|3分,4|4分,5|5分';
}
]]>
</om:elite>
<script>
//<![CDATA[
s = #{currentScore};
number=#{scoreCount};
titles = '#{titles}';
isAccess=#{isAccess};
window.onload = function (){
score_init();
};
function score(score) {
#{jsName}.#{methodName}(score,callBack);
}
function callBack(msg){
s=msg.substring(0,msg.lastIndexOf('|'));
score_init();
DWRUtil.setValue('currentScore',msg.substring(0,msg.lastIndexOf('|')));
DWRUtil.setValue('scoreCount',msg.substring(msg.lastIndexOf('|')+1));
}
//]]>
</script>
<c:forEach items="#{titles.split(',')}" var="a">
<img id="#{a.substring(0,a.lastIndexOf('|'))}" onmouseover="score_over(#{a.substring(0,a.lastIndexOf('|'))});"
onmouseout="score_init();" src="images/star_42_full.gif" title="#{a.substring(a.lastIndexOf('|')+1,a.length)}"
onclick="score(#{a.substring(0,a.lastIndexOf('|'))});"/>
</c:forEach>
<h:outputLabel value="用户评价分为" />
<span id="currentScore">#{currentScore}</span>
<h:outputLabel value=",已有" />
<span id="scoreCount">#{scoreCount}</span>
<h:outputLabel value="人评分" />
</ui:composition>
|
组件用到的js代码,下一小节我们会详细介绍submit.js的代码实现;下面3个js是dwr用到的,dwr/util.js和dwr/engine.js是dwr自带的脚本文件,dwr/interface/#{jsName}.js是dwr根据配置文件自动生成的,jsName值在dwr配置时用户自定义,从打分组件属性获取该值。
|
| |
elite语句,判断如果没有定义打分组件的属性,定义它们并且赋默认值
|
|
获取组件属性的值,如s是当前评分值即星星图标的显示值,用组件属性#{currentScore}的值给它赋值
|
| |
调用java类中的方法实现打分操作,并根据返回值刷新打分信息,约定返回值格式为"当前评价分值|当前已评分人数",java类要用户自己实现,在组件使用测试部分我们会介绍本文用到的java类
|
|
根据用户定义的打分组件属性titles中"分值|提示信息"数据组的个数(如"1|1分,2|2分,3|3分"为3个),在forEach循环中生成相应个数的星星图标, 利用变量a提取titles中的分值和提示信息分别使用,如提示信息title="#{a.substring(a.lastIndexOf('|')+1,a.length)}",分值#{a.substring(0,a.lastIndexOf('|'))}。在每个星星图标上定义了onmouseover,onmouseout,onclick事件来实现打分操作,onclick事件函数就是上文中的score(),调用java类中的方法实现,其余两个事件函数在submit.js中,在下小节详细介绍
|
| |
显示当前评价分值,从组件定义的属性获取
|
submit.js
function score_over(v) {
var i = 0;
var scores=titles.split(',');
for(i=0;i<scores.length;i++){
var data=scores[i].substring(0,scores[i].lastIndexOf('|'));
if(data<=v)
document.getElementById(data).src = 'images/star_42_full.gif';
else
document.getElementById(data).src = 'images/star_42_empty.gif';
}
}
function score_init() {
var i = 0;
var scores=titles.split(',');
var step=scores[1].substring(0,scores[i].lastIndexOf('|'))-scores[0].substring(0,scores[i].lastIndexOf('|'));
for (i = 0; i < scores.length; i++) {
var data=scores[i].substring(0,scores[i].lastIndexOf('|'));
if(s - data +step < step*0.25)
document.getElementById(data).src = 'images/star_42_empty.gif';
else if(s - data +step < step*0.75)
document.getElementById(data).src = 'images/star_42_half.gif';
else
document.getElementById(data).src = 'images/star_42_full.gif';
}
}
| |
星星图标的onmouseover事件的实现函数,即鼠标移到第n颗星星上时前n颗星星点亮,参数为星星图标代表的分值,根据参数判断将分值小于或等于该分值的星星点亮
|
|
星星图标的onmouseout事件的实现函数,也是星星图标显示分数的赋值函数,在鼠标离开图标或调用该函数时触发,因为规定星星的分值必须为等差递增,相邻分值间的差即为公差,本文的显示规则为大于(公差×0.25)点亮半颗星,大于(公差×0.75)点亮整颗星,如 titles="1|1分,2|2分,3|3分,4|4分"时公差为1,3.3分点亮三颗半星,3.8分点亮四颗星
|
<my:score titles="2|2分,4|4分,6|6分,8|8分,10|10分" isAccess="true" currentScore="#{score_dwr.currentScore}"
scoreCount="#{score_dwr.scoreCount}" jsName="score_dwr" methodName="score"/>
使用说明:titles为每颗星星的分值和提示信息,用户可根据需求自行设定,但格式必须是"分值|提示信息",每组信息之间用","隔开,而且分值应该是等差递增的一组数据,默认值为"1|1分,2|2分,3|3分,4|4分,5|5分";isAccess用来判断用户是否有打分权限,可根据自身系统的登录用户的权限给isAccess赋boolean类型的值,同样也可以用来限制每个IP地址只能进行一次评分等,默认值为true;currentScore是当前的用户评价分值,当前无人评分时默认值为0;scoreCount是当前已评分人的个数,当前无人评分时默认值为0;jsName是在js中用到的java类的名字,在dwr.xml中自定义;methodName是js调用的java类中的方法名。
dwr的使用:首先将dwr.jar拷入WEB-INF/lib中(本文工程中已拷入),再在web.xml中配置,添加代码如下:
<servlet>
<servlet-name>dwr-invoker</servlet-name>
<servlet-class>org.directwebremoting.servlet.DwrServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>true</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dwr-invoker</servlet-name>
<url-pattern>/dwr/*</url-pattern>
</servlet-mapping>
| |
这个参数DWR默认是false。如果选择true,可以查看到你部署的每个DWRclass,并且可以测试java代码的每个方法是否运行正常。为了安全考虑,在正式环境下要把这个参数设为false。
|
在web.xml同一目录下配置dwr.xml,其中value="dwr.Score_dwr"是调用的java类的名字,javascript="score_dwr"是在js中引用的java类的别名,可自定义。
<dwr>
<allow>
<create creator="new" javascript="score_dwr">
<param name="class" value="dwr.Score_dwr" />
</create>
</allow>
</dwr>
java类中主要函数代码分析:score()方法用来在js中被调用实现打分操作,完成本次打分信息的存储和返回当前打分信息;create()将本次打分信息存入数据库中。
public String score(String score) {
float scoreSum = 0;
MyScore myScore = new MyScore();
myScore.setId(UUID.randomUUID().toString());
myScore.setScore(Integer.valueOf(score));
// 向数据库表添加本次打分记录
create(myScore);
// 从数据库表查询所有打分记录,计算当前评价分值
List<MyScore> demoScores = findAll();
if (demoScores.size() > 0) {
for (MyScore data : demoScores)
scoreSum += data.getScore();
scoreSum = scoreSum / demoScores.size();
}
int scoreNum = demoScores.size();
String status = String.format("%s|%s", scoreSum, scoreNum);
return status;
}
public void create(MyScore Score) {
Connection cn = null;
PreparedStatement statement = null;
try {
Class.forName("org.hsqldb.jdbcDriver");
cn = (Connection) DriverManager.getConnection("jdbc:hsqldb:res:/db/hsql/score", "sa","");
String sql = "insert into MYSCORE values (?,?)";
statement = (PreparedStatement) cn.prepareStatement(sql);
statement.setString(1, Score.getId());
statement.setInt(2, Score.getScore());
statement.executeUpdate();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if (statement != null)
statement.close();
if (cn != null)
cn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
| |
根据从js中传入的参数将本次打分操作的分值信息存入数据库中,create()具体实现在下面介绍,findAll()的实现类似,此处不再解释,如果您感兴趣请下载工程(composition_score.zip)
|
| |
函数的返回值,格式为"当前评价分值|当前已评分人数"
|
|
为了方便用户下载工程直接使用,本文用内存数据库来存储数据,用户可选择适合的其他数据库或文件形式
|
|
|