Number Guess Example - Template Edition

This example shows the remote version of the number guessing game demonstrated in the previous example, but now we change the server side to use Javascript Templates instead of JSP to render the HTML page, and use Javascript Templates to render the cheat message as well.

The end result is a 100% Javascript application from the client and server-side logic to the templating language used to create HTML content, again both on the server and client-side. This allows the Java developer to create the backend application exposing a set of reusable services or interfaces which have no dependency on Servlets or any other presentation library. From those services, the web developer can create the entire view layer in Javascript, both server and client portions. No more complicated JSP with tons of scriptlets the Java developer has to help with. The Javascript Templates is a very concise and easy to use template language that makes HTML content generation easy and consistent, allowing you to split up the application function between server and client side based on requirements and not on levels of expertise.

In this example, the cheat message is displayed where the hint usually is rather than as a popup. The Javascript on both the server and client are new as are the Javascript Templates.

Flow Code

The usual number guessing flow code has been modified to intercept the Struts forward and render the code directly. Here is what the new parts of the flow code looks like:

flow.load("/templates/template.js");

...

// This function intercepts the forward back to Struts and renders the content
// directly using Javascript Templates
function renderTemplate(page, bizdata) { 
   // if rpc call, use old forward, otherwise use template
   if (page == "n/a") {
       return _oldForwardAndWait(page, bizdata);
   } else {
       var k = new Continuation();
       
       // Use default ttl value from continuation manager
       var timeToLive = 0;
       var kont = new WebContinuation(flow, k, lastContinuation, timeToLive);
       
       bizdata.contid = kont.id;
       res = struts.response;
       stream = struts.servletContext.getResourceAsStream("/WEB-INF/templates/"+page+".jt");
       if (stream != null) {
           text = new String(stream.getText());
           html = text.process(bizdata);
           res.writer.print(html);
           res.writer.close();
       } else {
           res.sendError(res.SC_INTERNAL_SERVER_ERROR, "Unable to find page "+page);
       }    
       
       suicide();
       return kont;
   }
}

// Replace old forward method with ours, but keep a reference to it so we can
// call it for remote flow responses
this._oldForwardAndWait = _forwardAndWait;
this._forwardAndWait = renderTemplate;

The _forwardAndWait() function is an internal function that Struts Flow calls to do the actual forward and continuation creation. We are replacing it as the forwardAndWait() function has useful logic for handling remote function calls that we don't want to duplicate. This process will most likely change as Javascript Templates are better integrated into Struts Flow. Our new renderTemplate() function ensures it is a real page request then generates the HTML by processing the passed template page and data.

Javascript Templates Presentation (Server Side)

The guess.jsp has been replaced by guess.jt which has basically the same content, but follows the Javascript Templates format rather than JSP. The client-side cheat() function retrieves the cheat.jt template on the server, then uses it to process the results from the server-side cheat() remote function call. The results of the template processing replace the hint message.

<html>
<head>
  <title>Struts Flow number guessing game</title>
    <script type="text/javascript">
  <!--
function init() {
    this.contid = "${contid}";
    this.client = new ClientFlow("guess.do");
}   
function cheat() {
    hint = document.getElementById("hint");
    result = client.call("cheat", contid);
    req = client.xmlHTTPRequestObject();
    if (req) {
      req.open ("GET", "cheat.jt", false);
      req.send (json);
      hint.innerHTML = req.responseText.process(result);
    }
    contid = result.contid;
}
    -->
  </script>
  <script type="text/javascript" src="clientFlow.js" />
  <script type="text/javascript" src="template.js" />
</head>
<body onload="init()">

  <h1>Guess the Number Between 1 and 10</h1>
  
  <h2 id="hint">${hint}</h2>
  
  <h3>You've guessed ${guesses} times.</h3>
  
  <form method="post" action="guess.do">
    <input type="hidden" name="contid" value="${contid}" />
    <input type="text" name="guess"/>
    <input type="submit"/>
    <input type="button" onclick="cheat()" value="Cheat" />
  </form>
  
  <a href="../index.html">Return to index</a>  
  
</body>
</html>

What is not obvious is Javascript Templates adds a process() function to the String object which allows us to process the response text of the cheat.jt call directly. The cheat.jt file looks like this:

The secret number is ${secret}. After applying a penalty, you have guessed ${guesses} times.