Skip to content
This repository

Bliss Templating System

Bliss is a JavaScript-based template engine, inspired by ASP.NET Razor and Play! Framework.

Using Bliss, developers can utilize two very familiar languages: HTML and JavaScript. There is no hurdle in learning a new syntax or language, it is just JavaScript and HTML. Using Bliss's compact and expressive syntax, it minimizes keystrokes and enables a more fluid template coding experience. A bonus is that you can edit the templates in most text editors without any special extensions (as long as you name your templates with a .js.html extension).

Bliss templates are simple text files containing JavaScript and HTML. The templates get compiled into JavaScript functions, which can be executed like any other function.

The following are the contents of orders.js.html:

@!(customer,orders)
<h1>Welcome @customer.name!</h1>

<ul>
@orders.forEach(function(order){
  <li>@order.title</li>
})
</ul>

You can then compile the template and receive the function to execute:

Bliss = require('bliss');
bliss = new Bliss();
template = bliss.compileFile('orders');
output = template(customer,orders);

You can also simply call render with the template name and the parameters:

output = bliss.render('orders',customer,orders);

Syntax: The '@' Character

Bliss uses the @ as the single special character. Every time this character is encountered, it indicates the beginning of a JavaScript statement.

Hello @customer.name!
       ^^^^^^^^^^^^^
        JavaScript

Bliss is able to detect simple statements. If you want to evaluate expressions, then you will need to wrap them in '(' and ')' (parenthesis):

Hello @(customer.firstName + customer.lastName)!
       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                     JavaScript

You may also use block statements within { and } (curly braces):

@{name = customer.firstName + customer.lastName}
Hello @(name)!

You may also escape the @ by using @@:

bob@@example.com

Template Parameters

A template is a JavaScript function, as such, you can declare parameters for the template on the first line of the template:

@!(customer,orders)

You may also use the arguments variable available to all JavaScript functions.

Looping

You can use JavaScript loop constructs (for, do-while, while), for comprehension and iterator functions.

<ul>
@products.forEach(function(product){
  <li>@product.name ($@product.price)</li>
})
</ul>

Which is similar to:

<ul>
@for(var p=0; p<products.length; p++) {
  @{product = products[p]}
  <li>@product.name ($@product.price)</li>
}
</ul>

Conditionals

If statements are available:

@if (items) {
  <h1>Nothing to display</h1>
}
else {
  <h1>@items.length items!</h1>
}

Reusable Blocks as Functions

You can declare functions within you templates and reuse them:

@function display(product) {
  <li>@product.name ($@product.price)</li>
}

<ul>
@products.forEach(display)
</ul>

Comments

Comments should be wrapped in @* and *@:

@***********************
 * Comment
 ***********************@

Composing (layouts, partials, etc.)

Template, being simple functions, can be composed in multiple ways using the @render() function. The first argument to @render() is the template file path. The subsequent arguments are the template parameters.

@render('layout',function(){
  @render('sidebar');
  ... contents ...
})

Layout

Let's declare a layout.js.html as:

@!(title,body)
<!DOCTYPE html>
<html>
  <head>
    <title>@title</title>
  </head>
  <body>
    @body()
  </body>
</html>

You will notice the template takes two parameter: a title and a body. The body will contain content which needs to be rendered, which means it should be a function. You can use the layout as:

@render("Home",function(){
  <h1>Home page</h1>
})

You may want additional blocks in the layout, such a a sidebar, so you can declare the layout as:

@!(title,body,sidebar)
<!DOCTYPE html>
<html>
  <head>
    <title>@title</title>
  </head>
  <body>
    <section id="sidebar">@sidebar()</section>
    <section id="body">@body()<section>
  </body>
</html>

And you can use the layout as:

@function sidebar() {
  <h1>Sidebar</h1>
}

@function body() {
  <h1>Home page</h1>
}

@render('layout',body,sidebar)

Partials / Imports / Embeds

Partials are pieces of content which can be included into a page. Usually, these are small reusable pieces.

<section id="sidebar>
  @render("sidebar")
</section>

Template Scope and Context

Templates run in their own scope, thus not making global variables inaccessible during template execution.

You can expose variables to the template by adding them to the template's context. You can define a global context, used by all templates or a per-template context.

Global Context

When you create a new Bliss instance, you can pass it a literal object containing a context. For example, I want to expose "underscore" as "_" to all templates:

bliss = new Bliss({
  context: {
    _: _
  }
});

Each template compiled by this Bliss instance will have access to "_" as a global variable.

Template Context

You may also specify a context during template compilation. The template context will be merged with the global context.

bliss.compileFile("mytemplate.js.html",{
  context: {
    _: _
  }
});

Configuration

The Bliss engine allows some customization. Bliss accepts a literal object containing configuration options when creating a new instance:

bliss = new Bliss({
  ext: ".js.html",
  cacheEnabled: true,
  context: {}
});

The following are the configuration options available:

ext

The file extension for template files. The default is js.html.

cacheEnabled

Enables/disables template caching. The default is true.

context

Context to be used by all templates compiled by the instance. The default is {}. See Global Context for more information.

Something went wrong with that request. Please try again.