在RichFaces用户论坛上,关于使用推送技术更新数据表单元格的问题有几个。默认的a4j:repeat示例对于初学者来说并不足以理解部分dataTable更新概念,因为默认使用了ajaxKeys功能(自动为来自动作事件的行生成键)。因此,我决定创建一个额外的示例,该示例将在下一个richfaces-demo版本中发布,并且更加高级。

用例

当用户查看投票结果页面时,表格中的值会随着人们的投票而更新。我将使用与a4j:repeat演示相同的基示例,但使用a4j:push演示中使用的Runnable接口实现来模拟周期性投票的放置。这将展示如何使用服务器端事件在客户端触发更新,而不是仅仅使用轮询,这会执行完整表单。在现实生活中,用例可能会更加复杂 - 用户可以监视大量表格的更新。该示例的另一个目标是展示如何在客户端编码和更新更改的单元格。

让我们创建一个简单的投票表单。它将显示包含每日选择的表格及其对应的投票数

<h:form>
<rich:dataTable value="#{choicesBean.choices}" var="choice"
      rowKeyVar="row" ajaxKeys="#{choicesBean.keysSet}">
      <f:facet name="header">
            <h:outputText value="Voting for favourite fruit" />
      </f:facet>
      <rich:column>
            <h:outputText value="#{row}" />
      </rich:column>
      <rich:column>
            <f:facet name="header">
                  <h:outputText value="choice name" />
            </f:facet>
            <h:outputText value="#{choice.label}" id="choiceLabel" />
      </rich:column>
      <rich:column>
            <f:facet name="header">
                  <h:outputText value="Votes Number" />
            </f:facet>
            <h:outputText value="#{choice.votesCount}" id="choiceVotes" />
      </rich:column>
</rich:dataTable>
</h:form>

现在我们需要创建两个简单的对象。一个是Choice对象,它包含选择标签投票计数

public class Choice {
      private String label;
      private int votesCount;
      public Choice(String label) {
            this.label = label;
            this.votesCount = 0;
      }
      //Getters and setters should be there

另一个是ChoicesBean,它将包含选择列表并负责收集更改信息。

public class ChoicesBean {
      private List<Choice> choices; //current state of voting
      private List<Choice> lastVotes; //collected votes to be added on update
      private Set<Integer> keysSet; // set of rows keys updated and needed to be updated at       //client side 
      public ChoicesBean() {
            //Choices and lastVotes lists instantiation
      }
      //Getters and setters should be there
}

然后我们指定这个ChoisesBean类将实现Runnable接口,并添加与a4j:push 演示中相同的startstoprun方法,以模拟投票过程。我们还将以类似的方式定义推送事件生产方法。以下代码需要添加:

...
private Thread thread;
private String updateInfo;
private boolean enabled = false;
PushEventListener listener;
...
public void addListener(EventListener listener) {
    if (this.listener != listener) {
        this.listener = (PushEventListener) listener;
    }
}
public synchronized void start() {
    if (thread == null) {
        thread = new Thread(this);
        thread.start();
        setEnabled(true);
    }
}
public synchronized void stop() {
    if (thread != null) {
        setEnabled(false);
        thread = null;
    }
}
public void run() {
    while (thread != null) {
        try {
            for (Choice choice : lastVotes) {
                choice.setVotesCount(DataTableScrollerBean.rand(0, 3));
            }
            listener.onEvent(new EventObject(this));
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

我想强调以下几点

  1. 新线程启动后,每十分钟将生成新的随机投票添加,并触发推送事件。
  2. 启用updateInfo仅用于向样本UI提供更多信息(为了保持控件启用/禁用并显示从服务器返回的最后更新)。

现在我们将添加按钮到页面,以启动投票模拟

<a4j:commandButton value="Start" action="#{choicesBean.start}"
      id="start" disabled="#{choicesBean.enabled}" ajaxSingle="true"
      reRender="push, stop, start" limitToList="true" /> 

<a4j:commandButton value="Stop" action="#{choicesBean.stop}" id="stop"
      disabled="#{!choicesBean.enabled}" ajaxSingle="true"
      reRender="push,start, stop" limitToList="true" />

最后是push组件

<a4j:push enabled="#{choicesBean.enabled}" interval="3000" timeout="3000"  
    eventProducer="#{choicesBean.addListener}" id="push" limitToList="true"  
    action="#{choicesBean.processUpdates}" reRender="choiceVotes, push, tempResults" />

现在我们只需定义一个processUpdates方法,该方法将在服务器端生成事件并触发更新请求后由push组件调用

public void processUpdates() {
      Set<Integer> keysForUpdate = new HashSet<Integer>();
      for (Choice choice : lastVotes) {
            if (choice.getVotesCount() > 0) {
                  int index = lastVotes.indexOf(choice);
                  keysForUpdate.add(index);
                  choices.get(index).increment(choice.getVotesCount());
            }
      }
      //updatedInfo generation.
      keysSet = keysForUpdate;
}

在检查是否生成了新的投票时,我们将实际改变的行的键填充到keysSet中。现在再次查看推送页面定义

reRender="choiceVotes... “

列组件内的h:outputText的ID会自动添加为重新渲染目标。在ChoicesBean中填充的keysSet被定义为表上的ajaxKeys。表格只对需要的单元格内容进行编码并传递给客户端以进行重新渲染!

因此,在运行此示例并按下启动按钮后,您将看到更新的单元格的周期性更新

完整演示代码可在richfaces-demo项目下的3.3.x社区分支中找到,地址为3.3.x社区分支,并可从匿名SVN下载。


返回顶部