Fork me on GitHub

The Variant Pattern

When working with passive templates a common task is to represent different types of data without polluting view logic with template parts. Say, we have a web shop and our search returned a number of products. Unfortunately not all of these products can be ordered for some reason. For another silly reason these products shall be visible anyway. Though, these products aren't order we will replace the order button with something that helps the user understand the situation.

Though, we could end up with a template like this:

<t:products>
<table>
  <tr>
    <th>Product Name</th> 
    <th>Price</th>                       
    <th>&nbsp;</th>
  </tr>
{v:target}
<t:product>
  <tr>
    <td>{v:name}</td>     
    <td>{v:price number="currency"}</td> 
    <td><input type="submit" value="Buy" name="buy{v:id}" /></td>
  </tr>
</t:product>
<t:silly_product>
  <tr>
    <td>{v:name}</td>     
    <td>--</td> 
    <td><img src="img/help.gif" alt="Help" title="Unbuyable Silly Product" /></td>
  </tr>
</t:silly_product>
</table>
</t:products>

We can see an extra sibling called target. As the variants represent alternative render targets I like to use an explicit one to clarify the situation. However, it's really important to use the same target for all variants, because otherwise, they would be sorted. Normal products first, 'silly products' behind. Java code is quite obvious I think.

  if (search.resultCount() == 0) { 
    // we can set arbitrary content to every location, no matter
    // whether it contains a sub-template or it's a simple location
    // mark. We could use a template defined in source or in another
    // template file.   
    template.set("products", msg.getString("nothing_found"));
    return;
  }
  
  Template productsTemplate = template.get("products");
  
  for (Product prod: search.getProducts()) {
    String prodName = prod.getName();
    if (prod.isSilly()) {
      productsTemplate.get("silly_product").set("name", prodName)
          .render("target");
    } else {
      productsTemplate.get("product").set("name", prodName)
         .set("price", prod.getPrice()).set("id", prod.getID())
         .render("target");
    }
  }
  
  productsTemplate.render();

This presents nothing hard to understand. Just don't forget to render to the target location.

Have fun

Bernd Ebertz

Head, Founder and chief technology evangelist of
jproggy.org
Java, and all Java-based marks are trademarks or registered trademarks of Oracle.
This site is not affiliated in any way with Oracle.