Introduction
Script# is a free tool that enables developers to author C# source code and compile it to JavaScript. In doing so, it helps to fully leverage the productivity of .NET tools and the Visual Studio IDE. While Script# delivers on what it promises, one thing that it lacks though is HTML templating that can fully leverage its power. SharpTemplate is a templating engine meant to fill this gap. This article is meant to be a tutorial for using the SharpTemplate HTML templating engine.
Background
HTML templates are a necessity in this era of rich web application development. Traditionally, HTML template engines were written in JavaScript and made use of RegEx parsing and Eval
to convert templates into HTML. While this works, the obvious disadvantages are low performance and debugging/maintenance difficulties. JavaScript generation tools like Script# have greatly offset the difficulty of developing huge sites using JavaScript, but they still leave the HTML templates to be managed by the traditional JavaScript based engines. This, to some extent, defeats the purpose of a static type checking tool like Script#. SharpTemplate aims to fill this gap by providing a compile time solution for HTML templating.
Prerequisite
You are expected to know about or have used Script# to some extent. Also, it would help if you can take a minute to visit the SharpTemplate site and go through its usage instructions.
To try out the sample application, you need to have Script# and SharpTemplate installed in your machine.
SharpTemplate tutorial
Generating Plain HTML
Plain HTML is the simplest to generate. A sample is shown below...
<%@ template name="RenderPlainHTML" args="" %>
<p>SharpTemplate has the below advantages...</p>
<ul>
<li>Compile time type checking</li>
<li>Leverages .Net tools and Visual Studio IDE</li>
<li>Super efficient compared to client side template engines</li>
</ul>
Let us understand the above template code first. The @template
tag indicates a template fragment. A SharpTemplate template file can contain multiple @template
tags. Each @template
tag would be converted into a static method in C#. The name
attribute in the @template
tag would be the name of the corresponding C# method. The args
attribute would be the arguments to the corresponding C# method, none in this case.
The C# code generated by SharpTemplate for the above template is as below...
public static string RenderPlainHTML(ArrayList b)
{
bool parentBuffer = (b != null);
b = b ?? new ArrayList();
b.Add(Script.Literal(@"'<p>SharpTemplate has the below advantages...<" +
@"/p><ul><li>Compile time type checking</li>" +
@"<li>Leverages .Net tools and Visual Studio IDE</li><li>" +
@"Super efficient compared to client side template engines</li></ul>'"));
return parentBuffer ? "" : b.Join("");
}
The important point to note here is that even though the args
attribute for the @template
tag is empty, SharpTemplate has emitted an argument, namely ArrayList b
. This argument is useful when nesting template calls (calling another template from within a template). As of now, we can ignore this and invoke the method with null
as its first argument value. What is happening within the method is very obvious. The HTML string gets added to the array list (treated as a string buffer) and is joined together at the end.
Interleaving C# code with HTML
SharpTemplate has borrowed heavily from ASP.NET with respect to its syntax. To embed code amidst the HTML, you use the <% %>
markup as shown below:
<%@ template name="IntersperseCode" args="int count" %>
<ui>
<%
for(int i = 0; i < count; ++i)
{
%><li>Script# with SharpTemplate is a great combo!</li><%
}
%>
</ui>
The template above takes a single argument, namely count
, and repeats the message that number of times. SharpTemplate emits all text/code embedded within <% %>
as it is (with appropriate indentation, if possible). The resulting C# code is shown below:
public static string IntersperseCode(ArrayList b, int count)
{
bool parentBuffer = (b != null);
b = b ?? new ArrayList();
b.Add(Script.Literal(@"'<ui>'"));
for(int i = 0; i < count; ++i)
{
b.Add(Script.Literal(@"'<li>Script# with SharpTemplate " +
@"is a great combo!</li>'"));
}
b.Add(Script.Literal(@"'</ui>'"));
return parentBuffer ? "" : b.Join("");
}
The important point to note is that the code embedded within the template can be any valid C#. It can access any of the standard APIs or your custom APIs. SharpTemplate doesn't do any interpretation of this code except for indentation inference.
Embedding C# expressions into HTML
Again, the expression syntax is borrowed from ASP.NET. To embed any string/numerical/boolean expression within the HTML, you enclose them within the <%= %>
tag, as shown below.
<%@ template name="EmbedExpression" args="int count" %>
<ui>
<%
for(int i = 1; i <= count; ++i)
{
%><li><%= i %> squared is <%= i * i %></li><%
}
%>
</ui>
The generated C# code is self explanatory.
public static string EmbedExpression(ArrayList b, int count)
{
bool parentBuffer = (b != null);
b = b ?? new ArrayList();
b.Add(Script.Literal(@"'<ui>'"));
for(int i = 1; i <= count; ++i)
{
b.AddRange(new object[] {
Script.Literal(@"'<li>'"),
i,
Script.Literal(@"' squared is '"),
i * i,
Script.Literal(@"'</li>'")
});
}
b.Add(Script.Literal(@"'</ui>'"));
return parentBuffer ? "" : b.Join("");
}
Having static helper methods/properties
At times, one may face the need to do some complex calculation or pre-computation that should be better kept outside of the template code itself. SharpTemplate allows emitting pure code to support such scenarios. A sample is shown below...
<%
private static int _Cube(int i)
{
return i * i * i;
}
%>
<%@ template name="EmbedExpression2" args="int count" %>
<ui>
<%
for(int i = 1; i <= count; ++i)
{
%><li><%= i %> squared is <%= i * i %>, cube is <%= _Cube(i) %></li><%
}
%>
</ui>
The first <% %>
block that appears in the template file before any of the @template
fragments would be treated as pure code, and would be emitted as it is. This block can be used to emit static methods and properties that may get used during the course of the template processing. Below is the C# code generated for the above template.
private static int _Cube(int i)
{
return i * i * i;
}
public static string EmbedExpression2(ArrayList b, int count)
{
bool parentBuffer = (b != null);
b = b ?? new ArrayList();
b.Add(Script.Literal(@"'<ui>'"));
for(int i = 1; i <= count; ++i)
{
b.AddRange(new object[] {
Script.Literal(@"'<li>'"),
i,
Script.Literal(@"' squared is '"),
i * i,
Script.Literal(@"', cube is '"),
_Cube(i),
Script.Literal(@"'</li>'")
});
}
b.Add(Script.Literal(@"'</ui>'"));
return parentBuffer ? "" : b.Join("");
}
Nested templates
In many scenarios, you may want to refactor some repeated templates into a template fragment of their own and call into them as required. The special argument ArrayList b
comes into play here. To invoke a template from within another template, you just put in the code within the <% %>
block and pass in b
as the first parameter to the call.
<%@ template name="Render" args="" %>
<div>
<% RenderPlainHTML(b); %>
</div>
<div>
<% IntersperseCode(b, 3); %>
</div>
<div>
<% EmbedExpression(b, 3); %>
</div>
<div>
<% EmbedExpression2(b, 3); %>
</div>
The corresponding C# code is shown below:
public static string Render(ArrayList b)
{
bool parentBuffer = (b != null);
b = b ?? new ArrayList();
b.Add(Script.Literal(@"'<div>'"));
RenderPlainHTML(b);
b.Add(Script.Literal(@"'</div><div>'"));
IntersperseCode(b, 3);
b.Add(Script.Literal(@"'</div><div>'"));
EmbedExpression(b, 3);
b.Add(Script.Literal(@"'</div><div>'"));
EmbedExpression2(b, 3);
b.Add(Script.Literal(@"'</div>'"));
return parentBuffer ? "" : b.Join("");
}
The HTML generated by invoking Render(null)
is shown below (indentation added for clarity)...
<div>
<p>
SharpTemplate has the below advantages...</p>
<ul>
<li>Compile time type checking</li>
<li>Leverages .NET tools and Visual Studio IDE</li>
<li>Super efficient compared to client side template engines</li>
</ul>
</div>
<div>
<ui>
<li>Script# with SharpTemplate is a great combo!</li>
<li>Script# with SharpTemplate is a great combo!</li>
<li>Script# with SharpTemplate is a great combo!</li>
</ui>
</div>
<div>
<ui><li>1 squared is 1</li><li>2 squared is 4</li>
<li>3 squared is 9</li></ui>
</div>
<div>
<ui><li>1 squared is 1, cube is 1</li><li>2 squared is 4,
cube is 8</li><li>3 squared is 9, cube is 27</li></ui>
</div>
Other tidbits
To register a namespace that is being referenced within the template code, you can put in code similar to that shown below towards the beginning of the template file before any code/template blocks:
<%@ register namespace="System.XML" %>
To register an alias, you can put in code similar to that shown below towards the beginning of the template file before any code/template blocks:
<%@ register alias="SysScript = System.Script" %>
If for some reason you want to change the default namespace under which the C# template class is generated, you can use the code shown below (at the beginning of the template file):
<%@ set namespace="AnoterNamespace" %>
Points of interest
Using SharpTemplate instead of other client-side templating engines has hugely helped save cost and boost developer productivity.
History
- 16th Jan 2010: Initial post.