nant lets you generate your html templates with plain javascript (browser and server).
Features
- No need to learn another language, just old good javascript
- Tags are simple POJOs (Plain Old Javascript Object) and can be manipulated with ease before stringification
- Uses all language constructs and programming techniques to define your reusable building blocks
- Attach your own methods to specific Tags using Mixins
Get started
On the Browser :
It's just one file - nant.js
- to import in your page.
...
On the server
npm install nant
Then
// get the Html Templates namespacevar ht = ht;var html = ht;
So what's this ? A new Templating Language ?
Obviously no; Instead javascript is the templating language.
Html tags are exposed as functions in the ht
namespace and tag attributes are passed as arguments.
var html = htinput type: 'text' name: 'myinput' required: 'true';console;
Tag body is passed as an optional argument
var html = ht;console;
String body
You can pass nested tags as body (note you don'need to call toString()
on nested tags)
htform id: 'myform' class: 'myclass' ht;
If you pass a function as body, it will be called upon rendering (with the parent tag as parameter)
{ return ht;} htform id: 'myform' class: 'myclass' myBody;
if you need to embody multiples tags, simply list them in order
htform id: 'myform' class: 'myclass' htlabel for: 'myinput' 'My input' htinput id:'myinput' name: 'myinput' 'Help text' someFunction;
You can also group body tags in arrays
htform id: 'myform' class: 'myclass' ht htlabel for: 'myinput' 'My input' htinput id:'myinput' name: 'myinput' ht;
css class attribute can be defined as an array
htinput class: 'class1' 'class2'
or a conditional object, only members with a truthy value will be picked
htinput class: class1: 1 < 2 class2: 1 > 2 )
you may also use array of both string/conditional object
htinput class: 'myclass' class1: 1 < 2 class2: 1 > 2 )
Tag manipulation
all methods of the ht
namespace returns an objet of type Tag
;
the Tag
's prototype exposes a few methods; this is useful if you want to manipulate the tag object before calling .toString()
.attr( attrName [, attrValue ] )
get/set current attribute value
var div = htinput name: 'myinput' class: 'myclass' ; div; // 'myinput'div; // ['myclass']div; // div.attr('name') === 'newName'
note how class attribute was converted to an Array; internally all tags maintains an Array
instance for class
attribute
.attr( [ attrName1, attrName2, ...] )
When passed an array, returns an object with given attributes
var div = htinput id:'myid' name: 'myname' placeholder: 'My Input' ;div; // { id:'myid', name: 'myname' }
.attr( object )
if you pass it an object, tag's attributes will be extended with object's members
var div = htinput name: 'myinput' class: 'myclass' ; div;div; // 'myid'div; // ['myclass', 'class1']
.hasClass()
used to check if tag instance references a css class
var div = htinput name: 'myinput' class: 'myclass' class1: 1 === 1 class2: 1 !== 1 ; div; // truediv; // truediv; // falsediv; // truediv; // false
.toggleClass()
is another familiar method to to toggle on/off css class references
var div = htinput name: 'myinput' class: 'myclass' class1: 1 === 1 class2: 1 !== 1 ; div; // div.hasClass('myclass') == false div; // div.hasClass('class2') == true div; // nothing changes as class1 is already enabled div; // div.hasClass('myclass') == true && div.hasClass('class2') == false
.match( selector )
Another useful method to check weather a tag matches the given selector; note only a small subset of css selectors is supported at the moment You can also supply a function (see example below) to perform tag matching
exemple
var divTag = ht;var inputTag = htinput id: 'myinput' class: 'control' 'col' ; // tag name selectorsdivTag; // trueinputTag; // truedivTag; // trueinputTag; // true // class names, idsdivTag; // truedivTag; // truedivTag; // truedivTag; // truedivTag; // trueinputTag; // true // uses custom matching methoddivTag;
.children( [ selector ] )
returns all direct children of the current tag; if provided, selector will be used to filter out the result
.find( selector )
returns all descendents (including non-direct children) matching the given selector
Mixins
Mixins allows attaching custom methods to selected tags
for example, suppose you have a data-model
attribute you want to apply to all input tags
//First you define you mixin function { return this;} // Then you attach it to all <input/> tagsnant;
therefore you can call input
tags with the dataModel
method
htinput name: 'myinput' ;
the exact signature of the nant.mixin()
method is
nant.mixin( selector, mixinFn, [mixinName] )
examples
{ ... } // define 'dataModel' methods on all <input/> and <select/> tagsnant;
{ this;} // define 'name' method on all tagsnant;
{ this
Object attributes (aka angular/knockout/... users)
nant supports passing nested objects as attribute values, this is useful in some cases (if you're working with data-binding libs like angular or knockout)
ht
Note also that camelCased
tag attribute names are transformed to their dash-delimited
countreparts (ie objAttr
become obj-attr
)
Sometimes, when working with data-binding libs, we have to pass expressions in the object attribute that will be evaluated later by the lib. observe this angular example
...
we can't write this object straight into our code because the expressions will be evaluateed directly
// Error, strike and bold members will get evaluated right now, probably raises an error if deleted or some aren't in the scopeht
instead use nant.uq(expr)
to build an unquoted expression
// Correct , strike and bold members will get evaluated laterht
Defining Custom Tags
=======================
Simply, use nant.makeTag
to make a tag builder function
// It's better to define custom tags in their own namespacevar ns = nantns = {};// define your custom element inside the namespacensmyElement = nant //Later you can use your tag functionvar myHtml = ht
If isVoid
parameter is true, then any body provided to the tag function will be ignored and closing tag (</myelement>
) will not be generated upon tag stringification
Tutorial: Bootstrap forms
the following exemple builds a twitter bootstrap form
htform class: 'form-horizontal' role: 'form' ht ht;
As we are simply using javascript, we are free to structure our templating the way we want. So we can also write
var bt = nantbt = {}; bt { var body = Arrayprototypeslice; return htform class: 'form-horizontal' role: 'form' body ;} bt { return ht} var myHtml = bt
Lets review the last example, w've reusable bootstrap tags to build form elements.
You may have noted that the grid's columns layout (all those col-sm-*
classes) is hard coded inside the templates.
What if we want to move layout defintion (col-sm-*
classes) outside ? we can make changes on form layout once and then apply it to all our form template.
// Form layout definitionvar layout = label: class: 'col-sm-2' input: class: 'col-sm-10' offset: class: 'col-sm-offset-2' // Form group apply layout def to its input and labelbt { input = ht; iflabel label = label; else input = input; return ht} var myHtml = bt
See how we've decoupled form layout and form field définition.
We can do better by abstracting away more bootstrap grid concepts
{ thiscols = cols; thismedia = media; thislabel = class: 'col-'+ thismedia + '-' + thiscols0 ; thisinput = class: 'col-'+ thismedia + '-' + thiscols1 ; thisoffset = class: 'col-'+ thismedia + '-offset-' + thiscols0 ;} var layout = 210 'sm'; bt { input = ht; iflabel label = label; else input = input; return ht} var myHtml = bt
You can go ever further to acheive better reusability; Because you're in the javascript land, you can apply your favourite desgin patterns.