Underscore.js is a highly useful complement to JavaScript’s rather sparse standard library. In a pinch, Underscore gives you simple templating, too. This post explains how it works and gives tips.
npm install underscoreThe only drawback is that you can’t use the name _ in the REPL. You need to use a name such as u:
> var u = require("underscore");In modules, you can use the name _ and in the following examples, we pretend that that is possible in the REPL, too.
_.template(templateString, data?, settings?)templateString holds the template. settings allow you to override the global settings. If you omit data, you compile a template that has to be applied to data as a function:
> var t1 = _.template("Hello <%=user%>!"); // compile > t1({ user: "<Jane>" }) // insert data 'Hello <Jane>!'First, you compile a template function t1, then you use that function to produce a string. You can also specify the parameter data and directly insert it into the template, without an extra compilation step:
> _.template("Hello <%=user%>!", { user: "<Jane>" }) 'Hello <Jane>!'
& < > " ' /Example:
> _.template("Hello <%-user%>!", { user: "<Jane>" }) 'Hello <Jane>!'
var t2 = _.template( "Users: <%_.forEach(users, function (u) {%>" + "<%=u%>, " + "<%})%>" );t2 is used as follows.
> t2({ users: [ "Huey", "Dewey", "Louie" ]}) 'Users: Huey, Dewey, Louie, 'By referring to the index of the current element, you can avoid the trailing comma:
var t2 = _.template( "Users: <%_.forEach(users, function (u,i) {%>" + "<%if (i>0) {%>, <%}%>" + "<%=u%>" + "<%})%>" );Interaction:
> t2({ users: [ "Huey", "Dewey", "Louie" ]}) 'Users: Huey, Dewey, Louie'
var t2 = _.template( "Users: <%_.forEach(users, function (u,i) {%>" + "<%if (i>0) { print(', ') }%>" + "<%=u%>" + "<%})%>" );
_.template("Hello <%=user%>!", { user: "<Jane>" }) _.template("Hello <%=obj.user%>!", { user: "<Jane>" })Using obj makes it easier to check whether a property exists. Compare – the following two templates are equivalent.
<%if (typeof title !== 'undefined') {%>Title: <%=title%><%}%> <%if (obj.title) {%>Title: <%=title%><%}%>The variable holding the data object is obj by default, but can be configured. Below, we use the name data, instead.
_.template("<%if (data.title) {%>Title: <%=title%><%}%>", null, { variable: "data" });If you specify a variable name in this manner, the properties of data won’t be available as variables. That has the advantage that Underscore won’t have to use a with statement (see Sect. 3) and the template function will be faster.
var tmpl = _.template("..."); _.forEach(objects, function (obj, index) { tmpl(_.extend({ _showSeparator: index > 0 }, obj)); });
_.templateSettings = { interpolate : /\{\{(.+?)\}\}/g };Interaction:
> _.template("Hello {{user}}!", { user: "<Jane>" }) 'Hello <Jane>!'You can change the syntax of each kind of insertion. Note, though, that syntax will be handled in the order escape, interpolate, evaluate. Hence, you cannot use more specific syntax for a “later” kind. For example, additionally introducing {{!...}} for evaluation would not work, because it would be preempted by interpolation.
... <script> var clientTmpl = <%= _.template(clientTmplDef).source %>; </script> ...One of the parameters of the above server-side template is the property clientTmplDef which holds the definition of a client-side template.
_.template("templateString")The above template is compiled to the function
function (obj) { var __p = []; // (Some code omitted here) with (obj || {}) { __p.push('templateString'); } return __p.join(''); }Let’s look at a more complex example that shows you how insertion works:
_.template("a <%-esc%> b <%=inter%> c <%eval%> d");Compiled into a function:
function (obj) { var __p = []; function print() { __p.push.apply(__p,arguments); } with (obj || {}) { __p.push('a ', _.escape(esc), ' b ', inter, ' c '); eval; __p.push(' d'); } return __p.join(''); }