Papa's Perspective

Code Reuse with External Templates and Knockout.js

External templates allow you to reduce the code in your main HTML page, and reuse it in multiple places.

Model-View-ViewModel, data binding and separation of concerns are just some of the reasons that Knockout.js, which I introduced in a recent column ("2 Great JavaScript Data-Binding Libraries," February 2012), has become a popular data-binding library in the Web community.

Knockout supports external templating libraries such as jQuery templates, but, starting with Knockout 2.0.0, it also includes its own native templating engine.

Templating has become second nature to many developers. Whether you use Model-View-Controller (MVC) or XAML or JavaScript, there's often a need to separate a section of structure into its own unit of work or template.

In HTML, it's common to have a structure that you want to repeat in many places. Whenever you see a structure that needs to be reused, templates should pop into your head.

Another common use for templates is in object hierarchies where you need to dive into a list of items and want to use the same structure for each item.

Inevitably your app will pile up the templates and you'll want to keep them in a separate file. Separating the templates into files has a few immediate advantages. First, it can reduce the structure in your main HTML file (less to look at is easier to understand). Second, just-in-time (JIT) templates allow you to grab them as needed and leave the others on the server.

Templates Everywhere
The Knockout native template engine offers multiple ways to implement templates. You can name templates and refer to them in a script tag by their id. This is a popular technique used by other engines such as jQuery templates. Another option is to use anonymous templates inline. I prefer anonymous inline templates when my template won't be reused because it keeps my HTML flowing, as opposed to having to go find a script tag somewhere else (which breaks my flow).

The following code shows a simple Knockout anonymous inline template that implicitly creates a template for the <li> elements. Notice there's no name for the template because it's all inline, which is nice and clean:

<ul class="smallList" data-bind="foreach:products">
  <li data-bind="css: {selected: isSelected}"  
    class="selectable">
    <div data-bind="text: shortDesc, 
      click:$root.selectProduct">
    </div>
  </li>
</ul>

Your HTML page could also contain another template, perhaps to display the selected product's details. This master list and item details scenario is pretty common and templates make a lot of sense for it. Listing 1 shows the product-details template contained within a script tag.

The output for the HTML page is shown in Figure 1. The anonymous template is used in a list to display each product, which ends up rendering nine times. The script template is used to render the details of the product. The source for this sample is in the file 01-ko-anonymous-and-script-template.html in the code download.


[Click on image for larger view.]
Figure 1. The anonymous template is used in a list to display each product, which ends up rendering nine times. The script template is used to render the details of the product.

External Templates
The previous example puts the templates inline and in script tags, but they're still in the same file. If these templates are moved to separate files, they can be reused. One way to tackle this is to store the templates in another file, wrap them in script tags, give them each an id and write a function that loads the templates via AJAX. The function has to ensure the Knockout bindings do not apply until all templates are returned, and handle other errors that could arise. Fortunately, there's a way to do this that requires practically no coding effort.

Jim Cowart recently wrote a plug-in for Knockout called Knockout.js External Template Engine. You can find the source for this library on GitHub, or you can use NuGet to add a reference to it from your Visual Studio project (which is my preference). This library extends the native template engine built into Knockout so you don't have to alter your code. It works with jQuery templates, too, if you go that route. For this example, I'm going to stick with the Knockout native templates.

Linking to the External Template Engine
The quickest way to get up and running is to grab the package using NuGet and add a link to the koExternalTemplateEngine_all.js file in your HTML page. Make sure you put the koExternalTemplateEngine file after the links to jQuery and Knockout, because it depends on both of those libraries:

<script src="/scripts/jquery-1.7.1.js" 
  type="text/javascript"></script>
<script src="/scripts/knockout-2.0.0.js" 
  type="text/javascript"></script>
<script src=
  "/scripts/koExternalTemplateEngine_all.js" 
  type="text/javascript"></script>

There's also a minified version of the library that I recommend using in production. The koExternalTemplate-Engine_all.js file contains the TrafficCop and infuser libraries, too, which is a nice convenience. If you want to, you can link to the libraries separately, as they're all included in the NuGet package.

Ready, Set, Go
Once you've linked to the library, using it is simple. You can move the productList template containing the <li> tags to its own file in the /templates/productList.tmpl.html file. The productDetails template in the script tag can also be moved to its own file in /templates/productDetails.tmpl.html.

This removes the templates from the flow of the page and allows them to be reused.

The productDetails.tmpl.html file now contains the contents of the template without the script tag wrapper. This is nice because it allows Visual Studio and its great IntelliSense to shine through on the HTML. Also, with the template in another file, it can be retrieved as needed, JIT.

Knockout can refer to the template by name, where the convention is the name of the file. Most templates will follow a naming convention, so you can set the prefix or suffix of the templates using some JavaScript:

infuser.defaults.templateSuffix = ".tmpl.html";

This JavaScript indicates that by default, all templates will have a suffix of .tmpl.html.

This means the Knockout code can refer to the template by the name of the file, without the extension.

Another common technique is to put all of the templates in a special folder. The following code shows how you can set the default folder for the templates:

infuser.defaults.templateUrl = "/templates";

These defaults can be overridden as needed, so I recommend using them for the most common template settings.

You can also use a template simply by referring to its name by string (this code retrieves the productList.tmpl.html template in the /template folder):

<ul class="smallList" data-bind="template:  
  {name: 'productList', foreach:products}">
</ul>

If you prefer Knockout's containerless, comment-based syntax, you can refer to a template as shown in this sample code, which also passes the selectedProduct to the template as its data context:

<!-- ko template: {name: 'productDetails',  
  data: selectedProduct}-->
<!-- /ko -->

This code will retrieve the productDetails.tmpl.html template from the /templates folder. If you want to override one of the default settings, such as the URL location of the templates, you can override them inline. This code indicates that the template is located in the /somewhere folder:

<!-- ko template: {name: 'productDetails', 
  data: selectedProduct,
  templateUrl: '/somewhere'}-->
<!-- /ko -->

The main settings for the Knockout external template library are templateSuffix, templatePrefix, templateUrl and loadingTemplate.content. The latter can be set to indicate the HTML content that will be displayed while the template is loading. You don't need to override the default loading content unless you want to follow a custom style. You can also override AJAX settings such as ajax.cache. The complete HTML source code for the external template sample can be found in 02-ko-external-template.html, while its JavaScript can be found in 02-ko-external-template.js.

Wrapping Up
Using external templates lets you reduce code in your main HTML page and reuse it in other places, which is great for maintenance. I highly recommend the Knockout External Template library; it's easy to integrate and unobtrusive. And that is Papa's perspective.

About the Author

John Papa is a Microsoft Regional Director and former Microsoft technical evangelist. Author of 100-plus articles and 10 books, he specializes in professional application development with Windows, HTML5, JavaScript, CSS, Silverlight, Windows Presentation Foundation, C#, .NET and SQL Server. Check out his online training with Pluralsight; find him at johnpapa.net and on Twitter at twitter.com/john_papa.

comments powered by Disqus

Featured

  • Diving Deep into .NET MAUI

    Ever since someone figured out that fiddling bits results in source code, developers have sought one codebase for all types of apps on all platforms, with Microsoft's latest attempt to further that effort being .NET MAUI.

  • Copilot AI Boosts Abound in New VS Code v1.96

    Microsoft improved on its new "Copilot Edit" functionality in the latest release of Visual Studio Code, v1.96, its open-source based code editor that has become the most popular in the world according to many surveys.

  • AdaBoost Regression Using C#

    Dr. James McCaffrey from Microsoft Research presents a complete end-to-end demonstration of the AdaBoost.R2 algorithm for regression problems (where the goal is to predict a single numeric value). The implementation follows the original source research paper closely, so you can use it as a guide for customization for specific scenarios.

  • Versioning and Documenting ASP.NET Core Services

    Building an API with ASP.NET Core is only half the job. If your API is going to live more than one release cycle, you're going to need to version it. If you have other people building clients for it, you're going to need to document it.

  • TypeScript Tops New JetBrains 'Language Promise Index'

    In its latest annual developer ecosystem report, JetBrains introduced a new "Language Promise Index" topped by Microsoft's TypeScript programming language.

Subscribe on YouTube