Interfaces

Project

Pages Html

NuGet packageOwin.Framework.Pages.Html
GitHub sourceOwinFramework.Pages.Html

Home |  Readme

Template system overview | OWIN Framework Pages

The templating system

Templates are a really important concept in building a website using this framework. Writing classes and decorating them with attributes to define pages, layouts, regions etc is very powerful and flexible, especially when those classes inherit from the standard implementation and override some of the virtual methods, but this technique has two important drawbacks: All of the website content is compiled into the asemblies which means you need to recompile and redepoly to change anything; Writing html and Javascript as strings that are passed to attribute constructors is ugly, and you don't get any help from the development tool (syntax highlighting and intellisense).

The templating system addresses these two issues by allowing you to place snippets of html, css and Javascript into separate resources then referencing them from region and layout elements.

Template Loaders

You can easily write your own template loader, or you can use one of the ones that are provided as standard. The job of the template loader is to retrieve templates from somewhere and pass them to a template parser, then optionally register them with the Name Manager.

The template loaders that are included in the standard package are:

OwinFramework.Pages.Html.Templates.FileSystemLoader

This template loader scans files in a directory within the file system, loads files and parses them then adds the parsed template to the name manager. When registering templates it maps the file path onto the template path using two configuration options that define the root of the folder tree and the root of the template path heirachy. By default the FileSystemLoader loads template files from a ~\Templates folder and maps them onto the root of the template path heirachy. What this means is that if you have a template file in ~\Templates\Profile\MyProfile.html this will be registered with the name manager as the /profile/myprofile template. Template paths are not case sensitive so you can also refer to this template as /Profile/MyProfile if you like.

Note that the Windows file system uses backslashes in folder paths but template paths use forward slash so the FileSystemLoader replaces backslash with forward slash when registering templates in the name manager.
Note that FileSystemLoader does not include the file extension when registering templates with the name manager. This is useful if you use the file extension to determine which parser to use and want to be able to switch parser without updating all references to the template.

The folder to scan, whether to include sub-folders or not, the mapping of file path onto the parser to use and periodic checking and reloading of templates are all configurable. When reloading is enabled the file system loader will calculate a hash for each template and only re-parse and re-register templates when the hash changes.

Below is an example of how to use the /span> FileSystemLoader. The full source code that this was taken from can be seen here.

var fileLoader = ninject.Get<OwinFramework.Pages.Html.Templates.FileSystemLoader>();
fileLoader.Load(markdownParser, p => p.Value.EndsWith(".md"));
fileLoader.Load(asIsParser, p => p.Value.EndsWith(".html"));
fileLoader.Load(mustacheParser, p => p.Value.EndsWith(".mustache"));

OwinFramework.Pages.Html.Templates.UriLoader

This template loader takes a URI that defines a template location, downloads the content from that URI, passes the content to a template parser and registers the template with the name manager. The Uri loader can also be configured to download the content periodically in which case it will calculate a checksum and only reparse and reregister the template if the checksum has changed.

Below is an example of how to use the /span> FileSystemLoader. The full source code that this was taken from can be seen here.

var uriLoader = ninject.Get<OwinFramework.Pages.Html.Templates.UriLoader>();
uriLoader.ReloadInterval = TimeSpan.FromHours(6);
foreach (var project in SiteMap.Instance.Projects)
{
    var repository = project.Repository;
    var repositoryName = repository.GitHubRepositoryName;
    var ownerName = repository.Owner.GitHubAccountName;
    var uri = new Uri("https://raw.githubusercontent.com/" + ownerName + "/" + repositoryName + "/master/" + project.ProjectName + "/readme.md");
    var templatePath = "/content/project/" + project.ProjectName + "/readme";
    uriLoader.LoadUri(uri, markdownParser, templatePath);
}
I strongly recommend using forward slash in template paths as shown in the above example but this is not required by the framework.

Template Parsers

Template parsers are responsible for taking a text document and turning it into an ITemplate implementation. You can write your own template parser and use it with any of the template loaders or you can use the built-in template parsers.

If you are writing your own parser you don't need to implement ITemplate, you just need to make your parser have an injected dependency on ITemplateBuilder and use it to build your templates. Take a look at the source code for the AsIsParser to see how simple this can be.

OwinFramework.Pages.Html.Templates.AsIsParser

This template parser does very little and yet is one of the most useful parsers. It simply takes the original text of the template resource and writes it directly into the page at runtime.

You can use this template parser whenever you have literal content in your template that you want to be directly inserted into the page content without any additional processing.

OwinFramework.Pages.Html.Templates.MarkdownParser

This template parser assumes that the template contains static text in markdown format. It parses the markdown and converts it into Html, then outputs the Html into the page each time the template is rendered.

See more details about the markdown template parser.

OwinFramework.Pages.Html.Templates.MustacheParser

This template parser assumes that the template contains html with data binding expressions in Mustache format. The data binding expressions will be replaced with data from the rendering context each time the template is rendered. You will need to add data providers to your solution to provide the data that is referenced in the data binding expressions.

See more details about the mustache template parser.

OwinFramework.Pages.Html.Templates.MultiPartParser

This template parser allows you to combine content from various parts of the page into a single template resource using a special separator between sections. For example you can combine CSS, html and Javascript templates into a single file and the MultiPartParser will separate out the different areas and render them into different parts of the page.

See more details about the multi-part template parser.

Building Templates Directly

Instead of using a loader to load template files then applying a parser to create the template, it is also possible to use the ITemplateBuilder to directly construct templates within your application. This is illustrated below:

var templateBuilder = ninject.Get<ITemplateBuilder>();
var template = templateBuilder.BuildUpTemplate()
      .AddElementOpen("p", "class", "dummy")
      .AddText("this-is-the", "This is the ")
      .AddDataField(a => a.Name)
      .AddText("application", " application")
      .AddElementClose()
      .Build();
  nameManager.Register(template, "/demo/template1");

Populating Layouts and Pages with Templates

Layouts and pages can have a [ZoneTemplate] attribute attached to the class to specify a template to render into a named region of the layout. When you do this for a template with a single part, the template will be rendered into the body of the page. If you do this with a multi-part template then it will be rendered into all parts of the page that are templated.

Populating Regions with Templates

To populate a region with templates attach one or more [RenderTemplate] attributes to it. Each attribute specifies a template path and the area of the page to render the template into.

Note that this technique only works with single-part templates. You can only attack one multi-part template and it already defines the page areas within the template itself.

Lets say for example I want to create a login form. The login form consists of some html, some CSS and some Javascript. It is convenient to create three separate templates because your development tool will provide syntax highlighting and intellisense within each file according to the type of content. These three templates need to be written to different parts of the page. You might create a region like the one below to accomplish this:

[IsRegion("login-form")]
[RenderTemplate("/forms/loginStyle", PageArea.Styles)]
[RenderTemplate("/forms/loginLayout", PageArea.Body)]
[RenderTemplate("/forms/loginModel", PageArea.Initialization)]
internal class LoginFormRegion { }

Multi-part vs Single Part Templates

Most template parsers take a text file, parse it into some html and then write that html into the body of the page when the page is rendered These are referred to as single part templates in this documentation. It is also possible to create templates that write different content into different parts of the page, i.e they can write styles and scripts into the head, html into the body and initialization scripts at the bottom of the page. These are referred to as multi-part templates.

When a single part template is rendered into a page or layout then it will always be rendered into the body of the page. In contrast the region provides more flexibility by allowing you to specify which part of the page you want to render into. The region also allows you to choose different templates to render into each part of the page. This is especially useful when creating self-contained reuasble pieces of UI functionallity because you can create different templates for the CSS Javascript and Html (possibly using different parsers) and combine these into a single region element by writing the templates into the appropriate places in the page.

If you want even more flexibility than regions provide, then you can create a component that can retrieve as many templates as you like from the name manager and render them however you want. This website it a good example of this technique; it has a component that maps the URL of the page onto the path of a template and renders that template making it possible to add new pages to the website just by adding new templates. Take a look at the source code to see how this works.