复合组件之 打分组件
2009-06-16 由 赵晓慧 发表   评论(3条)   有3076人浏览

1. 概述

新网站上线之后,很多用户评论说示例中的打分组件很好用,问AOM有没有这样的组件,我们做了这样一个打分的复合组件,用户可以根据自己的需要在系统中引用这个组件进行打分操作。

AOM为我们提供了原生Ajax支持的JSF引擎,但是用户同样可以自己定义AJAX实现,本文就会用到dwr(基于AJAX实现的在javascript中直接调用java方法),所以用户以前的知识积累在AOM同样可用。

2. 组件预期

上图为打分组件的预期实现,点亮的星星显示当前的评分,附有详细的文字说明已有几人评分和当前评价分值,用户可以通过点击星星图标来评分,每颗星星代表不同的分数值(分值从小到大),鼠标移到星星上时会出现提示信息,如第一颗星星是“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属性是必填的。

3. 组件做法

组件主要采用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的相关配置要用户自己实现。

下面我们就来介绍详细的代码实现。

3.1. 定义打分组件

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">
       
1<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[
            2if(!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[
          
3s = #{currentScore};
            number=#{scoreCount};
            titles = '#{titles}';
            isAccess=#{isAccess};
            window.onload = function (){
                score_init();
            };
          4function 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>
        
5<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="用户评价分为" />
         6<span id="currentScore">#{currentScore}</span>
           <h:outputLabel value=",已有" />
           <span id="scoreCount">#{scoreCount}</span>
           <h:outputLabel value="人评分" />
</ui:composition>
1

组件用到的js代码,下一小节我们会详细介绍submit.js的代码实现;下面3个js是dwr用到的,dwr/util.js和dwr/engine.js是dwr自带的脚本文件,dwr/interface/#{jsName}.js是dwr根据配置文件自动生成的,jsName值在dwr配置时用户自定义,从打分组件属性获取该值。

 

elite语句,判断如果没有定义打分组件的属性,定义它们并且赋默认值

3

获取组件属性的值,如s是当前评分值即星星图标的显示值,用组件属性#{currentScore}的值给它赋值

 

调用java类中的方法实现打分操作,并根据返回值刷新打分信息,约定返回值格式为"当前评价分值|当前已评分人数",java类要用户自己实现,在组件使用测试部分我们会介绍本文用到的java类

5

根据用户定义的打分组件属性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中,在下小节详细介绍

 

显示当前评价分值,从组件定义的属性获取

3.2. 事件的具体实现-submit.js

submit.js

1function 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';
   }
}

2function 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颗星星点亮,参数为星星图标代表的分值,根据参数判断将分值小于或等于该分值的星星点亮

2

星星图标的onmouseout事件的实现函数,也是星星图标显示分数的赋值函数,在鼠标离开图标或调用该函数时触发,因为规定星星的分值必须为等差递增,相邻分值间的差即为公差,本文的显示规则为大于(公差×0.25)点亮半颗星,大于(公差×0.75)点亮整颗星,如 titles="1|1分,2|2分,3|3分,4|4分"时公差为1,3.3分点亮三颗半星,3.8分点亮四颗星

4. 组件使用测试

4.1. 测试页面

<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类中的方法名。

4.2. 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>
    
1<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));
        // 向数据库表添加本次打分记录
       
1create(myScore);
        // 从数据库表查询所有打分记录,计算当前评价分值
         List<MyScore> demoScores = findAll();
        if (demoScores.size() > 0) {
            for (MyScore data : demoScores)
                scoreSum += data.getScore();
            scoreSum = scoreSum / demoScores.size();
        }
       int scoreNum = demoScores.size();
      2String status = String.format("%s|%s", scoreSum, scoreNum);
       return status;
  }
  public void create(MyScore Score) {
        Connection cn = null;
        PreparedStatement statement = null;
        try {
          
3Class.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)

 

函数的返回值,格式为"当前评价分值|当前已评分人数"

3

为了方便用户下载工程直接使用,本文用内存数据库来存储数据,用户可选择适合的其他数据库或文件形式

4.3. 测试效果

5. 小结

本文只是针对打分包装了简单的复合组件提供参考,用户可根据自己的需求修改或继续扩展,也欢迎用户将扩展后的复合组件继续和大家共同分享。

如果您对此组件感兴趣,可以直接下载使用或参考工程:(composition_score.zip)

另外需要提及的是,本文中用到了ELite特性,而 ELite 带给 AOM 远不只这些,如果你有兴趣,可以访问 www.operamasks.org

所有评论
vivian 2009-08-31 评论道:
图片是自己设定的,当然可以改变
peter 2009-08-07 评论道:
我可以换个图片或者是更改它的样式吗?或者是换形状?
peter 2009-08-07 评论道:
我可以换个图片或者是更改它的样式吗?或者是换形状?
1   共1页
您还没有登录,请登录后发表评论