Skip to content
The fastest + concise javascript template engine for nodejs and browsers. Partials, custom delimiters and more.
JavaScript CoffeeScript Ruby
Find file
#4 Compare This branch is 68 commits ahead, 38 commits behind olado:master.
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.
benchmarks
bin
examples
settings
test
.gitignore
Gemfile
Gemfile.lock
LICENSE-DOT.txt
README.md
compile.coffee
doT.js.orig
do_t.coffee
dot_core.coffee
express.coffee
package.json

README.md

doT.js

Created in search of the fastest and concise JavaScript templating function with emphasis on performance under V8 and nodejs. It shows great performance for both nodejs and browsers.

doT.js is fast, small and has no dependencies

Features:

  • custom delimiters
  • runtime evaluation
  • runtime interpolation
  • compile-time evaluation
  • partials support
  • conditionals support
  • array iterators
  • encoding
  • control whitespace - strip or preserve
  • streaming friendly
  • use it as logic-less or with logic, it is up to you

+ extra:

  • autoloading from dom elements & files (or custom functions)
  • caching
  • precompiling/exporting/importing cached templates
  • dynamic includes
  • type any spaces you want inside tags
  • precompilers (haml, slim)
  • written with coffee

Docs

Native Docs, live playground and samples:

http://olado.github.com/doT

Major changes

As of 1.0 compiler is an innstance of the DotCore class. So you can have several compilers with different settings in the single namespace.

DotCore provides basic methods for compliling and rendering templates. Compiling is just a sequence of actions (mangles) that is applied to template source to produce compiled template function. And compilling tags is just kind of mangle. The last mangle is usually will be creating of function with new Function.

So process of compiling template depends on settings of the compiler instance. Settings for original doT syntax are included. But you can modify them or define your ones. This should not be hard to write settings for Mustache or Jade for example.

In node.js you would not need to change anything, because require('doT') will load DotCore and create an instance with default settings.

But in browser you need to load dot_core, then settings file and then do_t file. or just compile it all together with

coffee --join dot.js --compile dot_core.coffee settings\original.coffee do_t.coffee

Incompability

original doT style:
{{~varname :value}}
{{~varname :value:key}}

old iterateFor style:
{{:varname :key:value}}

Current style:
{{~varname :value}}
{{:varname :value}}
{{~varname :key => value}}
{{:varname :key => value}}

Extra features docs

Before you continue

All described features did NOT make doT slower. There is benchmark folder, try compileBench.js and templatesBench.js yourself. All commits before 28de83c contain doT-original tests.

differences in configuration

  • You can not specify per template settings in doT.template() call (don't you want?).
  • You still can set it directly to doT.templateSettings before every call you want.
  • There is no templateSettings.append property. Instead of this set doT.templateSettings.startend directly to one of doT.startend.append (default), doT.startend.split or define your one.

Extend it!

Template compilation consist of serial mangles: resolving defines, processing tags, wrapping it into function's body and, finally, making it js function.

And you have easy access to all of this steps! doT.mangles is an object that contains functions to be called while mangling template from string to function. Every function executes in doT.templateSettings context, accepts 2 args: current template value and object with compile time variables. Here is an mangle definition that wraps function into with construction if doT.templateSettings.with is set to smth.

mangles['80_with'] = (str, compileParams) ->
  return str unless @with
  "with(#{if true == @with then @varname else @with}) {#{str}}"

This functions are called in alphabetical order of their keys. So this function will be executed first(it stores original template on input):

doT.mangles["00_test"] = (str, params) -> params.origTmpl = str
Add, modify and remove tags

Now it's much easier to modify or add new tags in your templates. I moved all tag's definitions into doT.tags object:

doT.tags.tagname = {
    regex: /your regex/,
    func: function(...){...} // to pass 2nd param to String.replace
}

Take a look at existing tags defenitions and you can easily define your new one or modify existing. Just change one of properties of doT.tags.

Tags are parsed in order of their names, so you'd better set 'evaluate' ({{ code }}) tag's name to something like z_evaluate to avoid it's regex matching other tags ( like {{? cond. }} or {{~arr :val}}).

Indeed one can easily implement http://mustache.github.com/ or other template engine.

Compile time tags stay untouched. Change their regexes in doT.templateSettings. All other regexes are moved to doT.tags.

Caching

There are transparent caching and autoloading. You can use autoloading in browser from DOM elements or in node from files.

Also you can compile templates into JSONP file to load them all together ready for use.

extra API

doT.addCached(id, tmplFunc)
doT.setCached(cacheObj)
doT.getCached() // returns cache object
doT.exportCached() // returns cache object as string
doT.render(tmplName, [args, ...])
// or
dot.render({ name: tmplName, args: argsArray })
/*render cached template or try autoloading.
Throws exception if no matching tmpl found.*/

Autoloading

You can set doT.autoload to you own function(tmplName) or use one of doT.autoloadDOM(opts) (default) or doT.autoloadFS(opts).

doT.autoloadDOM(opts) currently doesn't support any options. It looks for DOM element with id = tmplName and type = "text/x-dot-tmpl".

doT.autoloadFS(opts) used for serverside templating. You should specify options:

  • fs - filesystem module
  • root - path to templates directory
doT.autoload = doT.autoloadFS({
    fs: fs,
    root: '/path/to/dir'
})

It swaps dots with slashes so some.deep.file template will be looked for in /path/to/dir/some/deep/file.tmpl.

tmpls

{{@tmplName([args, ...])}} turns into doT.render('tmplName'[, args, ...]).

Note! To use it in serverside templates you should add doT to global scope like this

var doT = require( 'doT' )
global.doT = doT

{{:obj :val:key}} ... {{:}} iterates through obj with for .. in construction.

<script id="tmpl1" type="text/x-dot-tmpl">From tmpl1: {{=it.x}}; {{@tmpl2(it.y)}}</script>
<script id="tmpl2" type="text/x-dot-tmpl">From tmpl2: {{=it}}</script>
<script id="tmpl1" type="text/javascript">alert(doT.render('tmpl1', {x:1, y:2}))</script>

Should show 'From tmpl1: 1; From tmpl2: 2'

Dynamic includes

Instead of having split header.tmpl & footer.tmpl and including it in every template, you can have html.tmpl:

<!DOCTYPE html>
<html>
    <head>
        <title>doT</title>
    </head>
    <body>
{{@@content()}}
    </body>
</html>

and render it like this

doT.render( 'html', {
    items: [ 1, 2, 3 ],
    '_dynamic': {
        'content': { name: 'first' }
        //content: 'first'  // same result for now
    }
} )

// or
doT.render( 'html', {
    '_dynamic': {
        'content': {
            name: 'second',
            args: [{ item: {value: 'value'} }]
        }
    }
} )

If no args specified current arguments are used. So

  • in first case {{@@content()}} equals to {{=doT.render({name: 'first', args: arguments}}.
  • in second: {{=doT.render(it._dynamic[ 'content' ])}} and second template would be used.

You can specify field wich contains information about dynamic tamplates in doT.templateSettings.dynamicList (default to _dynamic).

Tips

I prefer keep data ('pure data', no matter where do you use it) and options (template specific data) separate for templates. Look at this like at the shell command: you pass data and options - instructions how to process this data. That's because one usually has data as JSON object from DB or somewhere else, and there should be no need to change this object before templating.

Even I think it's better way to use 2 arguments for template functions:

doT.templateSettings.varname = 'it, op'
doT.templateSettings.dynamicList = 'op.dynamic'

var opt = {dynamic: {content: {name: 'tmplname'}}}
var str = doT.render(tmplId, data, options)

I'll try to find out method to make it more essentual and flexible.

content_for

for tmpl:

{{>title}{{=one}} stuff{{>}}
more
{{>footer}}{{=two}} stuff{{>}}
and {{=three}}

doT.render(str).compile({one: 'some', two: 'other', three: 'more'}) will return

{
  _content: "more\nand more",
  title: 'some stuff',
  footer: 'other stuff'
}

compile options

Also compile-time option 'with' available (default to true). It wraps function body in 'with' construction which allows use properties directly (without it. prefix).

  • true sets doT.templateSettings.varname as argument for with
  • you can specify your own argument
  • false disables it

Warning! Be careful with this option, cause 'with' will fail if you run tmpl function with no (or less then specified) args.

compile.js [-b/--base dir] path[...]

Compiles templates from files in cache object. If path is directory it will walk it through recursively.

If --base specified template ids would be generated relative to this dir (/ replaced with .), otherwise just files basenames are used.

For -b root root/t1.tmpl root/t2.tmpl root/dir1/dir2/t1.tmpl ids would be [t1, t2, dir1.dir2.t1].

It requires optimist module, so install it before use.

Precompilers! .haml files will be passed through haml before compilation! To speedup this process you can install gems from included Gemfile. Then you can pass ruby_server: true to compiler and it'll run sinatra server to precompile templates. .slim is also supported in this mode.

License:

  • doT is an open source component of http://bebedo.com
  • doT is licensed under the MIT License. (See LICENSE-DOT)
Something went wrong with that request. Please try again.