JSF 2.3 websocket 如何与 Ajax 和 f:ajax actionlisenter 一起工作?
How JSF 2.3 websocket work with Ajax with f:ajax actionlisenter?
我试过JSF 2.3的新特性,其中一个吸引人的地方就是websocket。
我已经阅读了一些来自 mojarra 测试和 JSF 2.3 特定的示例代码 @Push
javadoc。
并且在使用 f:websocket
和 f: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 在这里也不起作用。
如何纠正这些?
你的具体问题是因为你的代码中没有任何地方明确地向那些 websockets 发送推送消息。如果您的尝试以某种方式可行,则 websocket 将在无限循环中继续向自身发送推送消息。这没有意义。
为了显式发送推送消息,您必须让您的代码显式调用 push.send(message)
,就像您已经对 <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="...">
就足够了。在现实世界的代码中,推送不是由同一页面中的某个命令按钮同步调用的,而是 异步 由某个业务事件调用的。否则,您可以只 return ajax 响应本身的结果。这节省了到服务器的额外往返。
我试过JSF 2.3的新特性,其中一个吸引人的地方就是websocket。
我已经阅读了一些来自 mojarra 测试和 JSF 2.3 特定的示例代码 @Push
javadoc。
并且在使用 f:websocket
和 f: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 在这里也不起作用。
如何纠正这些?
你的具体问题是因为你的代码中没有任何地方明确地向那些 websockets 发送推送消息。如果您的尝试以某种方式可行,则 websocket 将在无限循环中继续向自身发送推送消息。这没有意义。
为了显式发送推送消息,您必须让您的代码显式调用 push.send(message)
,就像您已经对 <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="...">
就足够了。在现实世界的代码中,推送不是由同一页面中的某个命令按钮同步调用的,而是 异步 由某个业务事件调用的。否则,您可以只 return ajax 响应本身的结果。这节省了到服务器的额外往返。