3

我尝试过体验 JSF 2.3 的新特性,其中一个吸引人的是 websocket。

我已经从 mojarra 测试和 JSF 2.3 特定的@Pushjavadoc 中阅读了一些示例代码。

并且在使用f:websocketf:ajax.

facelets 模板是:

   <h:panelGroup id="messagePanel" layout="block">
        <ul>
            <ui:repeat value="#{ajaxBean.messages}" var="m">
                <li>#{m}</li>
            </ui:repeat>
        </ul>
    </h:panelGroup>

    <h:form id="form">
        <h:commandButton 
            id="sendMessage" 
            action="#{ajaxBean.sendMessage()}" 
            value="Send Ajax Message">
            <f:ajax/>
        </h:commandButton>
    </h:form>
    <h:form>
        <f:websocket channel="ajaxChannel" scope="view">
            <f:ajax event="ajaxEvent" render=":messagePanel" />
        </f:websocket>
    </h:form>
    <h:form>
        <f:websocket channel="ajaxListenerChannel" scope="view">
            <f:ajax event="ajaxListenerEvent" listener="#{ajaxBean.ajaxPushed}" render=":messagePanel" />
        </f:websocket>
    </h:form>

    <f:websocket channel="commandScriptChannel" scope="view" onmessage="onCommandScript"/>
    <h:form>
        <h:commandScript name="onCommandScript" action="#{ajaxBean.commandScriptExecuted()}" render=":messagePanel"/>
    </h:form>

后端bean是:

@ViewScoped
@Named("ajaxBean")
public class AjaxBean implements Serializable {

    private static final Logger LOG = Logger.getLogger(AjaxBean.class.getName());

    @Inject
    @Push
    PushContext ajaxChannel;

    @Inject
    @Push
    PushContext ajaxListenerChannel;

    @Inject
    @Push
    PushContext commandScriptChannel;

    private List<String> messages = new ArrayList<>();

    public void ajaxPushed(AjaxBehaviorEvent e) throws AbortProcessingException{
        LOG.log(Level.INFO, "ajax pushed: " + e.toString());

        messages.add("ajaxListenerEvent is sent at: " + LocalDateTime.now());

        ajaxListenerChannel.send("ajaxListenerEvent");
    }

    public void commandScriptExecuted() {
        LOG.log(Level.INFO, "commandScriptExecuted pushed.");

        messages.add("commandScriptExecuted message is sent at: " + LocalDateTime.now());

        commandScriptChannel.send("onCommandScript");
    }

    public void sendMessage() {
//        LOG.log(Level.INFO, "ajax pushed by button: " + e.toString());

        messages.add("ajaxEvent is sent at: " + LocalDateTime.now());

        ajaxChannel.send("ajaxEvent");
    }

    public List<String> getMessages() {
        return messages;
    }

    public void setMessages(List<String> messages) {
        this.messages = messages;
    }

}

结果是第一个按钮可以根据需要触发ajax输出。但是带有监听器的 f:ajax 不起作用,并且 h:commandScript 在这里也不起作用。

如何纠正这些?

4

1 回答 1

4

您的具体问题是由于您在代码中没有明确向这些 websocket 发送推送消息而导致的。如果您的尝试以某种方式可行,则 websocket 将在无限循环中不断向自身发送推送消息。这没有意义。

为了显式发送推送消息,您必须让您的代码显式调用push.send(message),就像您已经对 that 所做的那样<h:commandButton>

<h:form>
    <h:commandButton value="Send push message" action="#{bean.sendPushMessage}">
        <f:ajax />
    </h:commandButton>
</h:form>
<h:form>
    <f:websocket channel="pushWithAjaxUpdate" scope="view">
        <f:ajax event="updateMessages" listener="#{bean.updateMessages}" render=":messages" />
    </f:websocket>
</h:form>
<h:panelGroup id="messages" layout="block">
    <ul>
        <ui:repeat value="#{bean.messages}" var="message">
            <li>#{message}</li>
        </ui:repeat>
    </ul>
</h:panelGroup>

@Inject @Push
private PushContext pushWithAjaxUpdate;

public void sendPushMessage() {
    messages.add("Push message is sent at: " + LocalDateTime.now());
    pushWithAjaxUpdate.send("updateMessages");
}

public void updateMessages() {
    messages.add("Ajax event is received at: " + LocalDateTime.now());
}

我了解您只是在进行试验,但应该说上述方法在现实世界的代码中也没有意义。在这个<f:websocket>特定的用例中是多余的,只需使用<h:commandButton><f:ajax listener="...">. 在现实世界的代码中,期望推送不是由同一页面中的某个命令按钮同步调用,而是由某个业务事件异步调用。否则,您也可以在 ajax 响应本身中返回结果。这节省了到服务器的额外往返行程。

于 2017-10-03T06:41:40.883 回答