.coffee
to .html
or aggregated templates.js
eliminates a lot of inbetween middlewareInspired by coffeecup, and ck, and mini-handlebars libraries.
5 template languages
7 are low-level logical
5 function recursions
7 the author dared not express
5 in the host language.
5 syntactic sugar
7 on low-level languages
5 ala coffeescript
7 brings template control to the
5 speed where it belongs.
# this line is only required within node
CoffeeTemplates = require 'coffee-templates'
# initialize new engine
engine = new CoffeeTemplates format: true
# provide template expression
doctype 5
html ->
head ->
meta charset: 'utf-8'
title "#{@title or 'Untitled'} | A completely plausible website"
meta(name: 'description', content: @description) if @description?
link rel: 'stylesheet', href: '/css/app.css'
style '''
body {font-family: sans-serif}
header, nav, section, footer {display: block}
'''
comment 'Stylus is not supported but CoffeeStyleshets might be'
script src: '/js/jquery.js'
coffeescript ->
$(document).ready ->
alert 'Alerts suck!'
body ->
header ->
h1 @title or 'Untitled'
nav ->
ul ->
(li -> a href: '/', -> 'Home') unless @path is '/'
li -> a href: '/chunky', -> 'Bacon!'
switch @user.role
when 'owner', 'admin'
li -> a href: '/admin', -> 'Secret Stuff'
when 'vip'
li -> a href: '/vip', -> 'Exclusive Stuff'
else
li -> a href: '/commoners', -> 'Just Stuff'
div '#myid.myclass.anotherclass', style: 'position: fixed', ->
p 'Divitis kills! Inline styling too.'
section ->
# A helper function you built and included.
breadcrumb separator: '>', clickable: yes
h2 "Let's count to 10:"
p i for i in [1..10]
# Another hypothetical helper.
form_to @post, ->
textbox '#title', label: 'Title:'
textbox '#author', label: 'Author:'
submit 'Save'
footer ->
# CoffeeScript comments. Not visible in the output document.
comment 'HTML comments.'
p 'Bye!'
locals =
title: 'Best website'
# render coffee template to html
console.log engine.render template, locals
engine = new CoffeeTemplates format: true, handlebars: true
template = ->
ul ->
for company in @companies
block "each #{company}", ->
li '{{this}}'
console.log handlebars_template = engine.render template,
companies: [ 'google', 'yahoo' ]
Outputs:
<ul>
{{#each google}}
<li>{{this}}</li>
{{/each}}
{{#each yahoo}}
<li>{{this}}</li>
{{/each}}
</ul>
Notice that while regular Mustache/Handlebars templates still compile, we took the liberty to engineer several improvements to the compiler:
function(arg..., cb) { cb(arg...) }
can be executed by a block<ul>{{$.each companies}}<li>{{this}}</li>{{/$.each}}</ul>
is valid<ul>{{jQuery.each people, (key, value)}}<li>{{key}}: {{value}}</li>{{/each}}</ul>
engine = new CoffeeTemplates format: true
console.log mustache_template = engine.render ->
block "each company, (name, data)", ->
h2 '{{name}}'
ul ->
block "each data.people", ->
li '{{this}}'
Outputs:
{{each company, (name, data)}}
<h2>{{name}}</h2>
<ul>
{{each data.people}}
<li>{{this}}</li>
{{/each}}
</ul>
{{/each}}
mustache_template = '{{each company, (name, data)}}<h2>{{name}}</h2><ul>{{each data.people}}<li>{{this}}</li>{{/each}}</ul>{{/each}}'
console.log template_fn = CoffeeTemplates.compile mustache_template
Outputs:
function anonymous(i) {
var o='',w=function(f,a){o='';f.apply(i, a);return o};return w(each,[i.company,function(name,data){o+="<h2>"+name+"</h2><ul>"+w(each,[data.people,function(){o+="<li>"+this+"</li>"}])+"</ul>"}])
}
This single function is a completely stand-alone version of your template, and is all that is needed to render the HTML.
Of course, this could also be used to render XML or some other markup, as well.
window.each = (o, cb) ->
for k of o
cb.apply o[k], [k, o[k]]
return
console.log html = template_fn company:
goog: people: ['Larry Page', 'Sergey Brin']
msft: people: ['Bill Gates']
Outputs:
<h2>goog</h2><ul><li>Larry Page</li><li>Sergey Brin</li></ul><h2>msft</h2><ul><li>Bill Gates</li></ul>
console.log templates = ''+CoffeeTemplates.compileAll
'views/users/index': mustache_template
'views/users/index_by_company':mustache_template
Outputs:
function anonymous(n,i) {
var o='',w=function(f,a){o='';f.apply(i, a);return o},t={}
t["views/users/index"]=function(){return w(each,[this.company,function(name,data){o+="<h2>"+name+"</h2><ul>"+w(each,[data.people,function(){o+="<li>"+this+"</li>"}])+"</ul>"}])}
t["views/users/index_by_company"]=function(){return w(each,[this.company,function(name,data){o+="<h2>"+name+"</h2><ul>"+w(each,[data.people,function(){o+="<li>"+this+"</li>"}])+"</ul>"}])}
return t[n].call(i)
}
From here you would normally save the function in a file like static/public/assets/templates.js
.
As usual, for the latest examples, review the easy-to-follow ./test/test.coffee.
Or try it immediately in your browser with codepen.