knockoff
v0.1.3
Published
KnockOff ========
Downloads
4
Readme
KnockOff
KnockoutJS to TAssembly compiler.
- Compiles a basic subset of KnockoutJS functionality to TAssembly, a simple JSON-based intermediate template representation.
- Builds a HTML5 DOM internally, ensures proper nesting.
- TAssembly performs context-sensitive escaping of all user-provided data.
- The overall solution is the fastest JS templating library in our micro-benchmarks, but yet provides the security benefits of much more expensive DOM templating libraries.
Usage
Simple example:
var ko = require('knockoff');
var template = ko.compile('<div data-bind="attr:{id:id}, text: body"></div>'),
model = {
id: "myId",
body: "some text"
};
console.log( template( model ) );
Compile to TAssembly for later execution:
var ko = require('knockoff');
var tassemblyTemplate = ko.compile(
'<div data-bind="attr:{id:id}, text: body"></div>',
{ toTAssembly: true }
);
// ["<div",["attr",{"id":"m.id"}],">",["text","m.body"],"</div>"]
console.log( JSON.stringify( tassemblyTemplate) );
Compile all the way to a function, and pass in TAssembly compilation options:
var ko = require('knockoff');
var options = {
// Define globals accessible as $.* in any scope
globals: {
echo: function(x) {
return x;
}
},
// Define partial templates.
// This one uses the global echo function defined above.
partials: {
userTpl: '<span data-bind="text: $.echo(name)"></span>'
}
};
// Our simple template using KnockOut syntax, and referencing the partial
var templateString = '<div data-bind="template: { name: \'userTpl\', data: user }"></div>';
// Now compile the template & options into a function.
// Uses TAssembly internally, use toTAssembly option for TAssembly output.
var templateFn = ko.compile(templateString, options);
// A simple model object
var model = {
user: { name: "Foo" }
};
// Now execute the template with the model.
// Prints: <div><span>Foo</span></div>
console.log( templateFn( model ) );
Partials are expected to be in KnockOff syntax, and will be compiled to TAssembly automatically.
KnockOff spec
KnockOff supports a subset of KnockOut functionality. The biggest differences are:
No reactivity. KnockOff aims for speed and one-shot operation.
Limited expression syntax. KnockOut supports arbitrary JS, while we restrict ourselves to literals (including objects), model access and function calls. The usual KnockOut model accessors are supported. In addition, a global
$
object is defined, which can be populated with theglobals
compile time option.
text
Emit text content. HTML-sensitive chars are escaped. Options is a single expression:
<div data=bind="text: textContent"></div>
See also the KnockOut docs for text
.
foreach
Iterate over an array. The view model '$data' in each iteration is each member of the array.
<ul data-bind="foreach: links">
<li data-bind="text: $data"></li>
</ul>
If each array element is an object, its members will be directly accessible in the loop's view model:
<ul data-bind="foreach: people">
<li><a data-bind="attr: { href: homepageURL }, text: name"></a></li>
</ul>
You can pass in the name of a partial instead of the inline template.
$index
, $parent
and other context properties work just like in
KnockOut.
See also the KnockOut docs for foreach
.
template
Calls a template (inline or name of a partial) with a given model.
<div data-bind="template: { name: 'person-template', data: buyer }"></div>
See also the KnockOut docs for template
.
with
The with binding creates a new binding context, so that descendant elements
are bound in the context of a specified object. It evaluates a nested block
iff
the model object is truish.
<div data-bind="with: person">
<span data-bind="text: firstName"></span>
<span data-bind="text: lastName"></span>
</div>
See also the KnockOut docs for with
.
if
Evaluates a block or template if an expression is true.
<div data-bind="if: displayMessage">Here is a message. Astonishing.</div>
See also the KnockOut docs for if
.
ifnot
Evaluates a block or template if an expression is false.
<div data-bind="ifnot: displayMessage">No message to display.</div>
See also the KnockOut docs for ifnot
.
attr
Emit one or more HTML attributes. Automatic context-sensitive escaping is applied to href, src and style attributes.
<a data-bind="attr: { href: url, title: details }">
Report
</a>
See also the KnockOut docs for attr
.
visible
Hides a block using CSS if the condition is falsy.
<div data-bind="visible: shouldShowMessage">
You will see this message only when "shouldShowMessage" holds a true value.
</div>
Currently this uses display: none !important;
inline, but we could also
add a class instead. Let us know which you prefer.
See also the KnockOut docs for visible
.
Virtual elements / container-less syntax
You can use Knockout's comment syntax to apply control flow bindings (if
,
ifnot
, foreach
, with
) to arbitrary content outside of elements:
<ul>
<li>This item always appears</li>
<!-- ko if: someExpressionGoesHere -->
<li>I want to make this item present/absent dynamically</li>
<!-- /ko -->
</ul>
See also the KnockOut docs for
if
and other
control flow bindings.
Model access and expressions
KnockOff supports a restricted set of simple JS expressions. These are a subset of KnockOut's arbitrary JS. A KnockOff expression will normally also be a valid KnockOut expression.
- Literals:
- Number
2
or3.4
- Quoted string
'Some string literal'
- Object
{foo: 'bar', baz: someVar}
- Number
- Variable access with dot notation:
foo.bar
- Array references:
users[user]
- Function calls:
$.i18n('username', {foo: bar} )
; nesting and multiple parameters supported
Expressions have access to a handful of variables defined in the current context:
$data
- current view model$root
- root (topmost) view model$parent
- parent view model$parents
- array of parent view models$parentContext
- parent context object$index
- current iteration index in foreach$
- globals defined at compile time; typically used for helper functions which should not be part of the model (i18n etc). This is an extension over KnockOut, which can be replicated there using expression rewriting.