Is a pretty common technique to write JSF composite components that
uses internally JavaScript code. The idea behind this post consist in
speculating the fact that JavaScript is a dynamic language that lets us modify
the DOM at runtime. Using the JSF client identifier and this JavaScript
capability can help us to solve the issue of repeating code for multiple
components. Sometimes, a good practice is to place the entire composite
component inside a div element whose ID is the JSF client identifier. Moreover,
you can identify and manage each div content directly from JavaScript.
For example, let's consider writing a composite component based on SSE.
The server role can be easily played by a Servlet (BetServlet.java). The
relevant code is listed below:
protected void
processRequest(HttpServletRequest request, HttpServletResponse response) throws
ServletException, IOException {
//content type must be set to
text/event-stream
response.setContentType("text/event-stream");
//encoding must be set to UTF-8
response.setCharacterEncoding("UTF-8");
// is not really necessary to use
try-with-resources
try (PrintWriter out = response.getWriter()) {
//send the open event
out.write("event:open\n");
out.write("data: OPEN CONNECTION \n\n");
for (int i = 0; i < 5; i++) {
out.write("data: Bet:" + i + " Lorem ipsum dolor sit
amet, consectetur adipisicing elit, sed do eiusmod ... \n\n");
}
//send the error event
out.write("event:error\n");
out.write("data: ERROR \n\n");
out.flush();
}
}
Further, we can write the composite component (bettips.xhtml):
<?xml
version='1.0' encoding='UTF-8' ?>
<!DOCTYPE
html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html
xmlns="http://www.w3.org/1999/xhtml"
xmlns:cc="http://xmlns.jcp.org/jsf/composite"
xmlns:h="http://xmlns.jcp.org/jsf/html">
<!-- INTERFACE -->
<cc:interface>
<cc:attribute name="server"
required="true"/>
<cc:attribute name="events"
required="false"/>
</cc:interface>
<!-- IMPLEMENTATION -->
<cc:implementation>
<!-- place here the JS code -->
</cc:implementation>
</html>
Now we can easily replace the <!-- place here the JS code --> with the JS code
for implementing the SSE communication. But, think that we place that code
here, and we want to use multiple components in page, as (index.xhtml):
<t:bettips
server="http://localhost:8080/SSESample/faces/BetServlet"
events="['open','error','message']"/>
<hr/>
<t:bettips
server="http://localhost:8080/SSESample/faces/BetServlet"
events="['message']"/>
<hr/>
<t:bettips
server="http://localhost:8080/SSESample/faces/BetServlet"
events="['open','error']"/>
These means that the JS code will be repeated for each component. In
order to avoid this issue we can exploit JavaScript closures. First we
"isolate" the JS code in a separate file (sse.js) and write some parameterized
and reusable JS functions. In our case, the JavaScript code that exploit SSE to
communicate with the server can be easily structured in two functions as below:
function
connectToServer(server, events, divid) {
if (server === null) {
alert("The server address cannot be
null ...");
throw new Error("The server address
cannot be null ...");
}
if (server === 'undefined') {
alert("You have to specify the server
address ...");
throw new Error("You have to specify
the server address ...");
}
if (!!window.EventSource) {
var eventSource = new EventSource(server);
addEvents(eventSource, events, divid);
} else {
// Ops!
alert("Cannot subscribe to this event
stream ...");
throw new Error("Cannot subscribe to
this event stream ...");
}
}
function
addEvents(source, events, divid) {
for (var i in events) {
if (events[i] === "error") {
source.addEventListener('error',
function(e) {
source.close();
//if (e.readyState ===
EventSource.CLOSED) {
//console.log(e.data);
document.getElementById(divid).innerHTML
+= e.data+"<br/>";
//}
}, false);
} else if (events[i] ===
"open") {
source.addEventListener('open', function(e) {
//console.log(e.data);
document.getElementById(divid).innerHTML +=
e.data+"<br/>";
}, false);
} else {
source.addEventListener(events[i],
function(e) {
//console.log(e.data);
document.getElementById(divid).innerHTML +=
e.data+"<br/>";
}, false);
}
}
}
Finally, the <!--
place here the JS code --> will be replaced with:
<div
id="#{cc.clientId}">
<script
type="text/javascript">
connectToServer('#{cc.attrs.server}', #{cc.attrs.events},
'#{cc.clientId}');
</script>
</div>
Done! The complete application is available here.
Niciun comentariu :
Trimiteți un comentariu