How T4 templates can help you with maintainable multi-page apps in JQM

We can utilize the power of T4 templates to generate a single HTML page which comprises of multiple separate HTML ‘children’ pages and a layout page, making multi-page apps more maintainable.

I’ve been playing around with the new recent release of JQuery Mobile which brings plenty of cool features to the table, so I’ve decided to build a small sample to help myself learn more about this wonderful mobile presentation framework.

During development I came across the simple choice that any developer needs to make when building apps with JQM: Single-page vs. Multi-page. Being a huge fan of the multi-page template, I’ve decided to go with the latter after weighing up the pros and cons based on my scenario.
It wasn’t long thereafter that I realized my dilemma about the maintainability of all the “internal” pages packed together within a single html file, so I began to research ways of keep using the power of multi-pages with the benefit of having each page maintained in a separate file.

I came across quite a few implementations, one of which uses AJAX to load all the external pages into the DOM at run-time. One can also use the Razor view engine to render separately maintained pages into one massive page including a layout page. The problem with these approaches is that we are limited to them when using a mobile framework like PhoneGap.

When using PhoneGap as a wrapper around your app, it will behave like a native app and no longer as a web-based app, which could have an impact on some functionality.

In my situation I was using PhoneGap, so the use of AJAX to load external local pages into the DOM wasn’t possible due to the CORS security measures and the use of the Razor view engine was also not possible due to the fact that I am using pure HTML files within a PhoneGap application.
After further brainstorming, I remembered my previous post about T4 templates and suddenly had a great idea of using T4 templates for rendering a single HTML page from multiple external pages based on a layout of my choice.

The concept

I wanted to keep things simple and stick to what I have, so the idea is to have a T4 template and an asset manifest which contains references to the external HTML and JavaScript assets as well as the layout for the single page. All the magic happens within the T4 template when it runs by rendering a single HTML page based on the configuration in the the manifest.

Talk is cheap, show me the code

The asset manifest:
<assets layout="SampleLayout.html">  
  <asset html="Page1.html" js="Page1.js"/>
  <asset html="Page2.html" js="Page2.js"/>
</assets>  

The T4 template:

<#@ template hostspecific="true" #>  
<#@ assembly name="System.Xml" #>  
<#@ import namespace="System.Xml" #>  
<#@ import namespace="System.Text" #>  
<#@ import namespace="System.IO" #>  
<#@ output extension=".html" #>  
<#  
string manifest = Host.ResolvePath("assetManifest.xml");  
XmlDocument document = new XmlDocument();  
document.Load(manifest);

string layoutFile =  document.SelectSingleNode("/assets").Attributes["layout"].Value;  
string layout = File.ReadAllText(Host.ResolvePath(layoutFile));

StringBuilder html = new StringBuilder();  
StringBuilder js = new StringBuilder();

foreach (XmlNode item in document.SelectNodes("/assets/asset"))  
{
    html.AppendLine(String.Format("\t<!--{0}-->",item.Attributes["html"].Value));
    html.AppendLine("\t" + File.ReadAllText(Host.ResolvePath(item.Attributes["html"].Value)));
    html.AppendLine();
    js.AppendLine("\t" + String.Format(@"<script type=""text/javascript"" src=""{0}""></script>",item.Attributes["js"].Value));

}

layout = layout.Replace("<!--Html-->",html.ToString()).Replace("<!--Js-->",js.ToString());

#>
<#= layout #>  

The Layout:

<!DOCTYPE html>  
<html>  
<head>  
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <meta name="format-detection" content="telephone=no" />
    <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi" />
    <meta name="msapplication-tap-highlight" content="no" /> 
    <link rel="stylesheet" type="text/css" href="css/vendor/jquery.mobile-1.4.0.min.css" />
    <title>My App</title>
</head>  
<body>  
<!--Html-->

    <script type="text/javascript" src="cordova.js"></script>
    <script type="text/javascript" src="js/vendor/jquery.js"></script>
    <script type="text/javascript" src="js/vendor/jquery.mobile.js"></script>
<!--Js-->

</body>  
</html>  

The and comment tags will be replaced by the assets referenced in the manifest.

To generate the single page, we simply right-click on the Index.tt file and select ‘Run Custom Tool

run

Note that this is done from within Visual Studio but you can also run the template from using the TextTransform command line utility.

The end result after running T4 tool:

<!DOCTYPE html>  
<html>  
<head>  
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <meta name="format-detection" content="telephone=no" />
    <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi" />
    <meta name="msapplication-tap-highlight" content="no" /> 
    <link rel="stylesheet" type="text/css" href="css/vendor/jquery.mobile-1.4.0.min.css" />
    <title>My App</title>
</head>  
<body>  
    <!--Page1.html-->
    <div data-role="page" id="Page1">
    <div data-role="header">
        Page 1
    </div>
    <div role="main" class="ui-content">
        Hello from Page 1
    </div>
    </div>

    <!--Page2.html-->
    <div data-role="page" id="Page2">
    <div data-role="header">
        Page 2
    </div>
    <div role="main" class="ui-content">
        Hello from Page 2
    </div>
  </div>

  <script type="text/javascript" src="cordova.js"></script>
  <script type="text/javascript" src="js/vendor/jquery.js"></script>
  <script type="text/javascript" src="js/vendor/jquery.mobile.js"></script>
  <script type="text/javascript" src="Page1.js"></script>
  <script type="text/javascript" src="Page2.js"></script>
</body>  
</html>  

This solution enabled me to have the maintainability of having multiple HTML pages with the benefits of the multi-page template in JQuery Mobile.

I took the liberty of also creating a Nuget package of this for anyone that might want to leverage this approach in their applications.

You can install it from the Nuget Package Manager console like this:

Install-Package HtmlMerge  

Enjoy!

comments powered by Disqus