Section 11.3. Browser-Side Templating


11.3. Browser-Side Templating

Pages, Presentation, Render, Template, Transform, View

Figure 11-5. Browser-Side Templating


11.3.1. Developer Story

Devi has just been assigned to make the user profile more verbose. The original version takes an XML document about the user and renders it all with a jungle of JavaScript code that accumulates a big HTML string amid if-then conditions and loops. Devi decides the first step is to refactor all that into a Browser-Side Template, isolating the HTML generation. As a result, introducing the new content becomes trivial.

11.3.2. Problem

How can you separate presentation from logic?

11.3.3. Forces

  • Generating HTML in the browser is a good way to isolate all presentation logic in one tier.

  • To render context-specific information within the browser, you need to rely on a dynamic mechanism. It's not feasible to just set an element's innerHTML property to point to a static HTML page.

  • Code gets complex and error-prone when you mix HTML generation with application logic. JavaScript is not well-suited to programmatically building up HTML strings.

11.3.4. Solution

Produce templates with embedded JavaScript and call on a browser-side framework to render them as HTML. The template contains standard HTML and allows context variables to be substituted in at rendering time, which gives it more flexibility than a static HTML page. In addition, you can also intersperse JavaScript code. For instance, generate an HTML table by running a loopone iteration for each row.

The templating idea has been used for a long time on the Web, which is evident in frameworks like Perl's HTML::Mason, Java's JSPs, and the layout of languages like PHP and ASP. All of these are syntactic sugar of the "killer app" variety. That is, they technically don't add any new functionality, but make life a whole lot easier for web developers. In the Java world, the standard complement to JSPs is servlets. Here's how you'd write a message in a servlet:

   package com.example.hello;   import java.io.IOException;   import java.io.PrintWriter;   import javax.servlet.ServletException;   import javax.servlet.http.HttpServlet;   import javax.servlet.http.HttpServletRequest;   import javax.servlet.http.HttpServletResponse;   public class HelloServlet extends HttpServlet {   protected void doGet(HttpServletRequest request,                        HttpServletResponse response)             throws ServletException, IOException {       response.setContentType("text/html");       PrintWriter out = response.getWriter( );       out.println("<html>");       out.println("<head><title>Hi Everyboddeee!</title></head>");       out.println("<body>");       out.println("  Your name is <%context.name %>\"" + request.getAttribute("name") +            "\".");       out.println("</body></html>");       out.close( );     }   } 

Not pretty. There are printing commands all over the place, escaped special characters, quote marks, and error handling. What happened to the simplicity of HTML? Code like this is best avoided because it mixes presentation with logic. The following JSP, using the standard JSTL library, achieves the same thing in a more lucid, maintainable style:

   <%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %>   <html>   <head><title>Hi Everyboddeee!</title></head>   <body>     Your name is <c:out value="$name" />.   </body>   </html> 

With many Ajax Apps, it's deja vu. Often, the browser receives a raw response such as a Plain-Text Message (Chapter 9) or an XML Message (Chapter 9) and must then render it as HTML, ruling out server-side templates. A common approach is to manually traverse the structure programmatically and create some HTML to be injected via some element's innerHTML property. The old "bucket of print statements" strikes again.

The solution here recapitulates in the browser what all those server-side templating frameworks offer. A templating framework, while not immensely difficult to implement, is probably not the sort of thing you'd want to write yourself. This pattern, then, is only made possible by the fact that frameworks already exist, and some are mentioned in the following examples. Here's a quick example of a template in Ajax Pages format (http://ajax-pages.sourceforge.net/):

   <html>   <head><title>Hi Everyboddeee!</title></head>   <body>     Your name is <%= name %>.   </body> 

The JavaScript template is in similar format to the JSP above and follows standard conventions of templates. That is:

  • Any code is contained inside <% and %> tags.

  • Any expression to be substituted in the HTML is contained inside <%= and %> tags.

  • To apply the template, create a processor and pass it a context. With Ajax Pages:

       var ajp = new AjaxPages( );   ajp.load("template.ajp");   var processor = ajp.getProcessor( );   element.innerHTML = processor({name: username}); 

11.3.5. Decisions

11.3.5.1. How will you obtain the template?

You'll generally pass the template to the processor as a string, but where does that string come from? The options here are the same as with Browser-Side XSLT (see earlier in this chapter): store it server side; hardcode it as a JavaScript string; and store it inside the HTML document. See the "Decisions" section in Browser-Side XSLT for more details.

11.3.5.2. Will the template include any code? How much?

Code in templates is somewhat frowned upon because it defeats the purpose of templates as a place to isolate the presentation. Certainly, more complex calculation are best performed in plain JavaScript or on the server side, but if managing the display requires some logic, a template is a reasonable place to put it. Examples include:


Loops

A collection is passed in to the template. The template outputs the entire collection by looping over the collection. It's much better for the template to perform the looping, rather than calling JavaScript, because there is usually some HTML before and after the loop. If JavaScript outputted that HTML, there would be too much coupling between the JavaScript and the template.


Conditionals

if-then conditions and switch statements are sometimes better performed by the template too. However, if the body of each branch is long, you might prefer to include a different template for each.

11.3.5.3. How to prepare the template's context?

In the case of Ajax Pages (http://ajax-pages.sourceforge.net/), you're allowed to pass in a context variable at rendering time. That's usually a good thing, as it provides an alternative to the template using global variables. The most obvious thing to do is pass some existing objects to the template, but sometimes the calling code can prepare some extra information. The aim is to perform as much processing as possible in the JavaScript code, so as to avoid any complex logic being performed within the template. For example, sometimes the template contains a simple if-then condition like this:

   <% if (context.credits ==  0) { %>     You need more credits!   <% } else { %>     You have <%=context.credits%> credits!   <% } %> 

The logic isn't especially complex, but scaling up with this approach can be problematic. As an alternative, let the JavaScript do some processing:

   var creditsMessage = credits ?     "You need more credits!" : "You have " + credits + " credits!"; 

The template then gives you a clear picture of the HTML it will generate:

   <%= context.creditsMessage %> 

11.3.6. Real-World Examples

11.3.6.1. Ajax Pages framework

Gustavo Ribeiro Amigo's Ajax Pages (http://ajax-pages.sourceforge.net/) is an open source templating framework. There's a basic blog demo (http://ajax-pages.sourceforge.net/examples/blog/index.html) on the project homepage (Figure 11-6).

Figure 11-6. Ajax Pages demo


11.3.6.2. JavaScript Templates framework

JavaScript Templates (JST) (http://trimpath.com/project/wiki/JavaScriptTemplates) is an open source templating framework from TrimPath. JST offers a richer set of functionality at this time, including:

  • Expression modifiers. The "capitalize" modifier, for instance, occurs in expressions like ${name|capitalize}.

  • A special syntax for loops and conditions.

  • Macros.

These features may be useful if you're relying heavily on templating, but there's also an argument that JavaScript alone is sufficient, so there's no need to add the burden of learning a second set of similar syntax.

Here's how a JST template looks:

   {for p in products}     <tr>         <td>${p.name|capitalize}</td><td>${p.desc}</td>         <td>$${p.price}</td>         <td>${p.quantity} : ${p.alert|default:""|capitalize}</td>     </tr>   {/for} 

With the string in a DOM element on the web page, you can apply the template with a one-liner:

   element.innerHTML = TrimPath.processDOMTemplate("templateElement", data); 

11.3.6.3. Backbase framework

Backbase (http://backbase.com/) is a commercial framework that extends standard XHTML with Backbase-specific tags (BXML). It's a more over-arching framework, as the tags are more powerful than standard templating engines, with many tags for widgets and visual effects available; e.g., <b:panel> tag for the user interface. See the "Real-World Examples" in Server-Side Code Generation (Chapter 12) for more details.

11.3.7. Code Refactoring: AjaxPatterns Templating Drilldown Demo

11.3.7.1. Initial version

In the Basic Drilldown Demo (http://ajaxify.com/run/portal/drilldown), the Drilldown menu itself is a bunch of HTML generated within JavaScript. The script picks up an XML file containing a specification for the current level and traversed it with standard JavaScript and XML access. See Drilldown (Chapter 14) for details, but as a quick summary, the XML file looks like the following:

   <category name="Overviews" parent="All Categories">     <items>       <link>         <url>http://en.wikipedia.org/wiki/AJAX</url>         <name>Wikipedia Article</name>       </link>       <link>         <url>http://www.adaptivepath.com/publications/essays/archives/000385.php </url>         <name>First Ajax</name>     </link>     <category name="Podcast Overviews" parent="Overviews" />   </items>   </category> 

The manual HTML generation looks like this:

   function onDrilldownResponse(xml) {     var category = xml.getElementsByTagName("category")[0];     var html="";     var categoryName = category.getAttribute("name");     html+="<div id='categoryName'>" + categoryName + "</div>";     //(Much more appending to html)     $("drilldown").innerHTML = html;   } 

11.3.7.2. Refactored to render from a template

For all the reasons just described, the HTML generation in the initial version is messy. The refactoring in this section introduces a template for the drilldown menu (http://ajaxify.com/run/portal/drilldown/template). Instead of the lengthy HTML generation in onDrilldownResponse( ), the method becomes a simple application of an AjaxPages template:

   function onDrilldownResponse(xml) {     var ajp = new AjaxPages( );     ajp.load("category.ajp");     var processor = ajp.getProcessor( );     $("drilldown").innerHTML = processor( {xml: xml} );   } 

So we're passing the entire XML string into the template, and the template's converting it to HTML. Let's see how that template (category.ajp) looks. First, it performs a little pre-processing on the XML string:

 <%   var category = context.xml.getElementsByTagName("category")[0];   var categoryName = category.getAttribute("name");   var parent = category.getAttribute("parent");   var items = category.getElementsByTagName("items")[0].childNodes; %> 

Then, it outputs the HTML. Note that looping and conditions are implemented with regular JavaScript, a fair reason to include JavaScript within a template.

   <div id='categoryName'><%=categoryName%></div>   <%     if (parent && parent.length > 0) {   %>       <div id='parent' onclick="retrieveCategory('<%=parent%>')">Back to <br/>       <%=parent%></div>   <% } %>   <%     for (i=0; i<items.length; i++) {       var item = items[i];       if (item.nodeName=="link") {         var name = item.getElementsByTagName("name")[0].firstChild.nodeValue;         var url = item.getElementsByTagName("url")[0].firstChild.nodeValue;   %>         <div ><a href="<%=url%>"><%= name %> </a></div>   <%       } else if (item.nodeName=="category") {         var name = item.getAttribute("name");   %>         <div class='category'               onclick="retrieveCategory('<%=name%>')"><%=name%></div>   <%       }     }   %> 

We now have exactly the same external behavior as before, but the templating approach has helped separate presentation from logic.

11.3.7.3. Refactored to improve template context

In the previous version, the context consisted of only one thing: the entire XML string. As mentioned earlier in "Decisions," it's sometimes worthwhile doing some preparation before passing the context over to the template. In the preceding example, the template begins by extracting out a few convenience variables from the XML. That's arguably okay, because it means the XML format is coupled only to the template, and not to the JavaScript. However, there's also an argument that the JavaScript should simplify the template's work by passing in a richer context. This further refactoring explores that avenue (http://ajaxify.com/run/portal/drilldown/template/prepareContext).

The change is quite small. The XML callback function now passes in a more detailed context:

   function onDrilldownResponse(xml) {     var ajp = new AjaxPages( );     ajp.load("category.ajp");     var processor = ajp.getProcessor( );     var category = xml.getElementsByTagName("category")[0];     $("drilldown").innerHTML = processor({       categoryName: category.getAttribute("name"),       parent: category.getAttribute("parent"),       items: category.getElementsByTagName("items")[0].childNodes      });   } 

The template no longer needs the convenience variables as it can now refer to properties of the context:

   <div id='categoryName'><%=context.categoryName%></div>   <%     if (context.parent && context.parent.length > 0) {   %>     <div id='parent' onclick="retrieveCategory('<%=context.parent%>')">Back to       <br/>     <%=context.parent%></div>   <%     }   %>   ...   <%     for (i=0; i<context.items.length; i++) {     ...   %> 

11.3.8. Alternatives

11.3.8.1. Browser-Side XSLT

In many cases, the browser receives an XML response. Where the browser is converting XML to HTML, Browser-Side XSLT (see earlier) is a very direct alternative to this Browser-Side Templating. Templating simplifies presentation but still requires parsing of the XML, which can be cumbersome. XSLT simplifies the parsing as well, being designed specifically for the purpose of transforming XML.

11.3.9. Related Patterns

11.3.9.1. XML Message

Templating is well-suited to transforming an XML Message (Chapter 9) into HTML.

11.3.9.2. JSON Message

Templating is well-suited to transforming an object from a JSON Message (Chapter 9) into HTML.

11.3.10. Metaphor

Think of a physical templatea document with most content already present, with a few blanks to populate with current values.




Ajax Design Patterns
Ajax Design Patterns
ISBN: 0596101805
EAN: 2147483647
Year: 2007
Pages: 169

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net