Schanuzer is largely compatible with Mustache and Handlebars templates. In most cases it is possible to swap out Mustache or Handlebars with Schanuzer and continue using your current templates.
Schanuzer is also very small and fast. It has the power of Handlebars but is almost the size of Mustage (12.1KB minified, ~4.89KB gZip) and therefore also perfectly suitable for mobile applications. Renderin with Schnauzer is about ~33% faster than with Handlebars, when using inline partials, up to 10x faster. Parsing is also blasting fast, almost as fast as rendering.
You can use schnauzer.js to render templates anywhere you can use JavaScript. This includes web browsers, server-side environments such as node, and CouchDB views.
schnauzer.js ships with support for both the CommonJS module API and the Asynchronous Module Definition API, or AMD.
Other than handlebars, schnauzer has 2 optional functions that get triggered with every single tag that gets rendered so the template can be kept alive even after the first rendering.
renderHook()
and loopHelper()
. With those functions it's possible to keep track of all the rendered variables and rendering functions including a special character %
set infront of every variable. This way, when used in the DOM, it's possible to overwrite parts of the rendered template after it was first rendered without having to re-render the whole template.
This is perfect for developing MVC like libraries/frameworks that need to partialy update HTML on the fly.
(Handlebars facade for Schnauzer) ... maybe back one day.
Below is a quick example how to use schnauzer.js:
var viewModel = {
title: "Joe",
calc: function ($1) {
return parseFloat($1) * 0.9;
}
};
var output = new Schnauzer("{{title}} spends {{calc 200}}").render(viewModel);
In this example Schnauzer()
is initialized with the template as first argument (options would be the second optional argument) and the render()
function that takes one parameters: the viewModel
object that contains the data and code needed to render the template.
new Schnauzer(templateOrOptions: String | { [key: String]: any }, options?: { [key: String]: any }) {
tags: ['{{', '}}'], // used tags: default is {{ }}
entityMap: { // characters to be escaped
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": ''',
'/': '/',
'`': '`',
'=': '='
},
escapeHTML: true, // if false, Schnauzer renders like all tags are set to {{{ }}}
helpers: { [name: String]: Function }, // short-cut for registerHelper
partials: { [name: String]: String | Function }, // short-cut for registerPartial
self: 'self', // name of initial partial
limitPartialScope: true, // sets limiting of scope inside partials (like in HBS)
renderHook: Function // called every time an inline | block element renders
loopHelper: Function // Loop cycle callback for Array | Object inside #each
})
.render(data: { [key: String]: any }, extraData: { [key: String]: any }): string
.parse(text: String): Function // returns a partial for re-cycling, re-usage in other instance
.registerHelper(name: String, func: Function): void
.unregisterHelper(name: String): void
.registerPartial(name: String, html: String | Function): Function // Function: pre-parsed
.unregisterPartial(name: String): void
.setTags(tags: [String, String]): void
.escapeExpression(string): String // returns escaped text according to entityMap
parse()
is only needed if the template was not passed to Schnauzer()
in the first place. This might be handy if you're not sure if this template will ever be used...
In render(data, extraData)
you can pass some extra data source needed for parsing your template. If renderer doesn't find the required data in data
then it looks inside extraData
. extraData
can be and opbject or an array of objects.
This can be very handy if you have, for example, an array of links to render where the root of the link is always the same (stored in extraModel) but the end of the link is different (stored in the array). So you don't have to put the root inside evey item of the array.
var data = {
links: [{
link: 'link1',
text: 'My first link',
}, {
link: 'link2',
text: 'My second link'
}]
}
var extraData = {
root: './some/path'
}
var output = new Schnauzer('{{#links}}<a href="{{root}}/{{link}}">{{text}}</a>{{/links}}')
.render(data, extraData);
All the functions used inside the model or being registered or passed in options as helpers can be used as inline or block elements that have the same arguments and scope:
var data = {
meaning: 'some more meaning',
foo: 'This would be',
helper: helper
}
function helper([$1, $2,...]) { // can also be passed as option or registered via .registerHelper()
var args = [];
var options = arguments[arguments.length - 1];
for (var i = 0; i < arguments.length - 1; i++) {
args.push(arguments[i]);
}
return $1 + ' ' + options.fn(this);
}
In this case you would get "This would be some text with some more meaning" in the block and "This would be " if used as inline helper.
$1 etc. represent the String passed with the block (here "foo").
options.fn()
is the text that was rendered if it was inside a block element (empty if inline usage), options.inverse()
the block after an else (if exists, else undefined).
Options inside helper functions work almost like with Handlebars:
options: {
data: { root: {…}, scope, parent, first, last index, key, number, length },
hash: {}, // keeps all the parameters as a hash
name: "", // name of the helper
fn?: ƒ (context, options), // only on block helpers; same as with Handlebars
inverse?: ƒ (context, options), // only on block helpers; same as with Handlebars
escapeExpression: ƒ(), // like Handlebars.escapeExpression
SafeString: ƒ(), // like Handlebars.SafeString
keys: ƒ(), // like window.Object.keys()
extend: ƒ(newObject, hostObject), // like Handlebars.Utils.extend
concat: ƒ(newArray, hostArray), // Concats 2 arrays
getDataDetails: ƒ(), // returns details of the data (arguments)
}
Inline helpers can be used for something like the following:
today: function() {
return new Date().toLocaleString();
}
All basic features of Schnauzer are explained in the Handlebars decumentation.
By default, when schnauzer.js first parses a template, it builds a tree of currying functions that keeps all data cached. The currying functions not only already hold the parsed HTML snippets but also the right key to the JSON being passed so it can concatenate strings on the fly. The rendering functions are highly optimised therefore Schnauzer currently renders 1/3 faster than Handlebars. Parsing is about 10x faster.
The new version 2.x.x has a new parser that is faster, it renders faster than the previous major version (from 65ms down to 52ms on a standard comparison test with 500 runs: 16K template + 1k partial and quite a bit of data), uses a lot less memory and allowes a more flexible way of using variables within tags (no limits on characters). It has better support (API) for "active rendering" after 1st rendering. All Schnauzer functionalities are now supported to be altered through the new API (renderHook, loopHelper). The "activate" API doesn't compromise speed any more if not used so you can have 99.99% of the high speed parsing/rendering if used only as a one-time template-rendering engine. Unfortunately the new parser is a bit bigger (1.02KB source) than the old one, but we profit now from more speed, less memory usage and more flexibility.