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
- exporting/importing cached templates
- dynamic includes
- type any spaces you want inside tags
- written with coffee
##Docs ###Native Docs, live playground and samples: http://olado.github.com/doT
###Extensions Additionally to the original doT the following 2 tag styles are allowed for loops:
{{~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 setdoT.templateSettings.startend
directly to one ofdoT.startend.append
(default),doT.startend.split
or define your one.
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
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 moduleroot
- 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' ])}}
andsecond
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'
}
##License:
- doT is an open source component of http://bebedo.com
- doT is licensed under the MIT License. (See LICENSE-DOT)