Friday, January 31, 2014

Using External Templates with Knockout.js

A guest post by Ryan Niemeyer, who has over 15 years of experience in the software industry working with primarily web-based technologies. He loves JavaScript, participating in the open source community, and trying to learn new things everyday. He is always willing to lend a helping hand to anyone that needs it. Since, 2010 Ryan has been involved with the Knockout.js project and writes about his experiences with it on his blog ().



When building a web application with , working with more than a handful of templates is a common occurrence. As the number of templates in your application grows, adding additional script tags for these templates becomes a pain point and a maintenance issue. As an alternative, having less templates by using control-flow bindings (if, with, and foreach) in larger blocks of markup is also not an ideal solution, as it does not promote reuse and has the same maintenance concerns. A better technique is to move each template's markup into individual files.




WHY EXTERNAL TEMPLATES?



Defining templates in external files has a number of advantages:



* Files with an html extension and normal markup are generally handled better by most editors than trying to edit markup inside of a script tag.

* You have more encouragement to create reusable templates, as the pain of adding and maintaining more script tags is removed.

* The purpose and focus of smaller, more modular files is easier to ascertain.

* You have less potential for merge conflicts than when using a single large file, since you will have a better chance to work in separate files.



AMD SOLUTIONS



The Asynchronous Module Definition format provides a way to define a JavaScript module and its dependencies with the ability to be asynchronously resolved. Using a module system like AMD can help organize your application and clarify the dependencies that each module needs. If you are using AMD, there are some nice options for pulling in externally defined templates.



DURANDAL



is a full-featured SPA (single-page application) framework built on top of Knockout and . Durandal provides a powerful compose binding that allows you to render an area by combining a view model and template. Both the view model and template can be defined in external files and Durandal provides guidance for building an optimized file with several build systems.



KNOCKOUT-AMD-HELPERS



is a lightweight library that I wrote to make Knockout's template engine AMD-aware. The template engine will then automatically load templates using an AMD loader's text plugin and provides some configurability for how the path to a template is generated. Additionally, the library provides a flexible module binding that can combine view models with templates. Templates can then be included in an optimized build by using the include functionality of . Knockout-AMD-Helpers has been tested with require.js and .



WITHOUT AMD



While there are some good options for using templates in AMD-based projects, AMD may not be an option for many applications. A project may have too much existing code that is not AMD-aware, it may use a different module-loading system, or you may prefer a more traditional namespacing approach over a specific module system. In this case, there are still options for supporting external templates.



STRING TEMPLATE ENGINE + BUILD STEP



One option that I have used in the past is a template engine that directly supports string based templates. is a simple implementation that allows defining templates in a ko.templates object. With this template engine in place, making a template available to the application would look like:



ko.templates.myTemplate = '';



Defining templates as strings in JavaScript, however, is not a good developer experience for the initial creation or maintenance of the template. Instead, templates can be defined in external files with a build step that creates a single, optimized file that includes calls to add each template's markup to the ko.templates registry. As a convention, the template's name can be derived from the filename and the resulting, optimized file can be concantenated with other files for a production build.



For my front-end build needs, I currently use , although you could accomplish building this file with a variety of tools. Here is a sample Grunt task for building a templates.js:



//take a group of external template files and create a single JS file //that adds the templates grunt.registerMultiTask("combineKOTemplates", function() { var path = require("path"), files = grunt.file.expand(this.data.src), result = ""; files.forEach(function(file) { //strip the extension to determine a template name var name = path.basename(file).replace(".tmpl.html", ""), //remove line feeds and escape quotes escapedContents = grunt.file.read(file).replace(/"/g ,"""). replace(/(rn|n|r)/gm, ""); result += "ko.templates["" + name + """] = "" + escapedContents + "";n"; }); grunt.file.write(this.data.dest, output); });



You could then use this task to generate one or more files from your template directories. The task configuration might look like:



grunt.initConfig({ combineKOTemplates: { main: { src: "src/templates/*.tmpl.html", dest: "src/js/templates/js" } } });



As an alternative to using the string-based template engine, you could also choose to add a script tag to the page for each template. Although, I feel that an option like the enhanced template engine is a cleaner route, than adding all of your application's templates as script tags to the page at run-time.



KNOCKOUT.JS-EXTERNAL-TEMPLATES PLUGIN



The plugin by is another option that can be configured to pull in templates from external files. The main drawback to this option is that it makes HTTP requests for each template. In certain scenarios, this technique could still be beneficial, but the author now encourages using a technique that can properly include the templates in an optimized file for production use.



CONCLUSION



Non-trivial Knockout applications are difficult to maintain in a single file of markup. There are a number of good options for pulling in HTML templates that are defined externally, without increasing the number of HTTP requests for a production build. Using one of these options can provide a better workflow and more satisfying development experience.



Look below for some great JavaScript resources from Safari Books Online.



Not a subscriber? Sign up for a .



SAFARI BOOKS ONLINE HAS THE CONTENT YOU NEED



teaches you the basics of programming with JavaScript, the worlds most used programming language. The tutorial is designed for the absolute beginner - no prior JavaScript programming experience is required in order to get the most out of this video training. You will start with learning what programming is, and specifically, what JavaScript is, how it it used, and its limitations. You will discover variables and data types, and how to take input and create output. Craig covers conditions, loops, arrays, sorting, functions, paramaters and debugging. You will even learn advanced concepts such as OOP, string manipulations, regular expressions and other programming patterns.



is a guide to JavaScript that focuses on good programming techniques rather than offering a mish-mash of cut-and-paste effects. The author teaches you how to leverage JavaScript's grace and precision to write real browser-based applications. The book begins with the fundamentals of programming--variables, control structures, functions, and data structures--then moves on to more complex topics, like object-oriented programming, regular expressions, and browser events. With clear examples and a focus on elegance, Eloquent JavaScript will have you fluent in the language of the web in no time.



teaches you how to create dynamic, interactive Web pages with the popular and ubiquitous JavaScript web programming language. Using a straightforward, step-by-step approach, each lesson in this book clearly and carefully walks you through basic concepts and techniques, and helps you learn the essentials of JavaScript programming from the ground up.
Full Post

No comments:

Post a Comment