My JSF Books/Videos My JSF Tutorials OmniFaces/JSF PPTs
JSF 2.3 Tutorial
JSF Caching Tutorial
JSF Navigation Tutorial
JSF Scopes Tutorial
JSF Page Author Beginner's Guide
OmniFaces 2.3 Tutorial Examples
OmniFaces 2.2 Tutorial Examples
JSF Events Tutorial
OmniFaces Callbacks Usages
JSF State Tutorial
JSF and Design Patterns
JSF 2.3 New Features (2.3-m04)
Introduction to OmniFaces
25+ Reasons to use OmniFaces in JSF
OmniFaces Validators
OmniFaces Converters
JSF Design Patterns
Mastering OmniFaces
Reusable and less-verbose JSF code

My JSF Resources ...

Java EE Guardian
Member of JCG Program
Member MVB DZone
Blog curated on ZEEF
OmniFaces is an utility library for JSF, including PrimeFaces, RichFaces, ICEfaces ...

[OmniFaces Utilities] - Find the right JSF OmniFaces 2 utilities methods/functions

Search on blog

Petition by Java EE Guardians

Twitter

joi, 22 octombrie 2015

Exploit JavaScript closures in JSF composite components

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

JSF BOOKS COLLECTION

Postări populare

OmniFaces/JSF Fans

Follow by Email

Visitors Starting 4 September 2015

Locations of Site Visitors