Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

monkberry

Package Overview
Dependencies
Maintainers
1
Versions
49
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

monkberry

JavaScript DOM Template Engine

  • 4.0.0-beta.5
  • Source
  • npm
  • Socket score

Version published
Weekly downloads
524
increased by482.22%
Maintainers
1
Weekly downloads
 
Created
Source

Monkberry - JavaScript template engine

Build Status

Monkberry compile template to JavaScript code for creating nodes with DOM API and helper methods for updating content of these nodes.

npm install monkberry --save

Features

  • Small, dependency free
  • Simple and minimalistic
  • Fully tested
  • One-way data flow
  • Precompiled templates
  • SourceMaps
  • Custom tags
  • Extremely fast!

Table of Contents

Example

Monkberry will compile this template:

<div>
  <h1>{{ title }}</h1>
  <p>
    {{ text }}
  </p>
</div>

To JavaScript code like this:

var div = document.createElement('div');
var h1 = document.createElement('h1');
var p = document.createElement('p');

div.appendChild(h1);
div.appendChild(p);

   ...

view.update = function (data) {
  h1.textContent = data.title;
  p.textContent = data.text;
};

Which can be used like that:

var view = monkberry.render('template');
view.appendTo(document.body); 

view.update({
  title: 'Monkberry',
  text: 'JavaScript DOM template engine'
});

Documentation

Getting Started

Monkberry has support for both browserify via monkberrify and for webpack via monkberry-loader.

Monkberry can be used like CLI tool. Install Monkberry globally:

npm install monkberry -g

To compile all templates into single JavaScript file with source map run next command:

monkberry --source-map --output template.js templates/*.html

Require generated template.js and monkberry.js files and mount template:

var monkberry = require('monkberry');
var template = require('./template.js');

monkberry.mount(template);

Render that view.

var view = monkberry.render('template'); 
// or
var view = monkberry.render('template', {...}); 

Attach generated DOM nodes to the page.

document.getElementById('root').appendChild(view.createDocument());

Now, to update data of view on page:

view.update({...});
// or update only what's needed
view.update({key: value});

Expressions

Monkberry perceives everything inside {{ and }} mustache as JavaScript expression.

<div class="greetings {{ visible ? '' : 'hidden' }}">
  Hello, {{ user.name + "!" }}
</div>

If, Else

Can be any valid JavaScrpt expressions.

{% if count < 0 || count > 10 %}
  ...
{% else %}
  ...
{% endif %}

Any number on variables in if:

{% if array.indexOf(search) != -1 %}
  ...
{% endif %}

Note what Monkberry update only one of if/else block.

{% if check %}
  Then {{ value }}!
{% else %}
  Else {{ value }}!
{% endif %}

Render that template:

var view = monkberry.render('example', {
  check: true,
  value: 'one'
});

View will be Then one!. When if update view:

view.update({
  check: false,
  value: 'two'
});

View will be Else two!. But if update only check, variable of then part will be same as before.

view.update({check: true});

View will be Then one!.

This is happens becouse Monkberry does not stores variables passed to update function, it stores only DOM nodes. Monkberry will update only one part of if/else.

For

Monkberry can loop other arrays and objects as well.

{% for array %}
  {{ name }}
{% endfor %}

In this form, body of for has access only for variables iterating on.

view.update({
  array: [
    {name: 'Anton'},
    ...
  ]
});

To access outer scope specify iterator name.

{% for user of array %}
  {{ user.name }}
{% endfor %}

Also key can be specified.

{% for key, user of array %}
  {{ key }}: {{ user.name }}
{% endfor %}

Default values

Render of view contains two phase: node creation and update of node contents with data.

var view = monkberry.render('template', data);
// Equals to:
var view = monkberry.render('template');
view.update(data);

Some times data for view does not available and it's use full to place come data as default. Best way to do it is use logical OR operator ||.

<div class="foo {{ modify || 'baz' }}">
    {{ content || "No content" }}
</div>

In this case on first phase of render view will be filled with default data:

<div class="foo baz">
    No content
</div>

Note if you will use some variable in right side of OR operator, what can't be used as default data.

{{ content || "No content" + foo }}

Filters

Any expression support filter statement.

Hello, {{ user.name | upper }}

To define that filter:

monkberry.filters.upper = function (text) {
  return text.toUpperCase();
};

Also Monkberry understand parameters for filters:

monkberry.filters.replace = function (text, from, to) {
  return text.replace(from, to);
};
{{ text | replace(/.../, '$1') }}

And allow to combine filters:

{{ text | lower | replace(/.../, '$1') | upper }}

That expression will be compiled to next JavaScript:

upper(replace(lower(text), /.../, '$1'));

Filters can be used in expressions, if and for statements.

Custom tags

Any template mounted to Monkberry can be called as custom tag.

monkberry.mount(require('./views/block.html'));

Inside another template possible to insert that block templace as custom tag:

<div>
  <block/>
</div>

One file can contains several definitions of custom tags:

<my-tag>
  ...
</my-tag>
<my-second-tag>
  ...
</my-second-tag>

Custom tags may contains variables:

<greet>
  {{ value }}, {{ name }}!
</greet>

To render that custom tag, specify variables as attributes:

<greet value="Hello" name="world">
<greet value="Hello" name="{{ user.name }}">

Spread attributes

Spread attributes allow easily convert object into node attributes.
The properties of the object that you pass in are copied onto the node's attributes.

<input {{...attr}}/>
var view = monkberry.render('template', {attr: {
  id: 'foo', 
  value: 'baz'
}});

You can combine it with other attributes.

<input {{...attr}} value={{ value }}/>

Note what later updates of attributes override previous ones.

view.update({value: 'baz'});
// ...
view.update({attr: {value: 'new baz'}}); // Will override previous value.

Spread operator also works well with custom attributes. In fact, this is best way to pass data into custom tag.

<my-tag {{...attr}}/>
<my-tag>
    <input type={{ type }} value={{ value }}>
</my-tag>

Importing

It is possible to require template within another template.

{% import './path/to/template.html' %}

    <template/>
 

Import statement will require that template and automatically mount it to monkberry.

Event Handling

There are a few ways to deal with event handling in Monkberry. Add event listener to node directly:

view.querySelector('.button').addEventListener('click', function (event) {
    ...
});

But this is difficult when dealing with conditions and loops (it's is possible to solve if using wrappers).

Better approach is to use event delegating.

view.on('click', '.button', function (event) {
    ...
});

Globals

Monkberry also support global variables. This is very usefull if using window variable inside of templates. Or if using translation function like this: {{ __('greeting') + userName }}.

To do it, you need to specify globals as array of variables names for compiler to pass. Read monkberry loaders docs for more info.

Prerender

To speedup render Monkberry can prerender DOM nodes to use them in future.

monkberry.prerender('template', 10); // Preprender template 10 times.

Then next render call will use one of these prerendered views:

monkberry.render('template', {...}); // Will use already created DOM nodes.

This is very usefull to do then browser waiting some xhr request.

To get info about prerendered template in runtime, use monkberry.getPoolInfo().

Note what prerender works only with first level DOM nodes. This means you need to manually prerender templates used in if/for tags. Look at block statement for example.

Pool

If you have big application with a lot of template within, it may be a problem what two templates have same names.

To solve this problem create a "pool of templates":

var pool = monkberry.createPool();

This allow to have templates with same names. createPool method creates new Monkberry instance.

Note what filters remain same.

Wrappers

Every template in Monkbeery when rendered can be "wrapped" by function.

For example we have a template logo.html:

<div>
  <i class="svg-icon"></i>
</div>

And we want to insert SVG nodes inside i tag on render. This is can be done via wrappers:

monkberry.wrappers.logo = function (view) {
  view.querySelector('.svg-icon').appendChild(svgIconNodes);
  return view;
};

Wrappers usefull to manipulate view's nodes, adding event listeners and a lot of other staff.

Transforms

Transformers allow to modify AST before compilation of templates. List of AST nodes can be founded here: ast.js Example of transform which trim whitespaces: whitespace.js

Add transforms to Monkbeery before compilation:

import { Compiler } from 'monkberry';
import { myTransform } from './myTransform';

var compiler = new Compiler();
compiler.transforms.custom = myTransform;

Parsers

Now Monkberry support only one type of parser, mustage like (monk named). But it can be extender with custom parsers. Every parser must return valid AST tree.

import { Compiler } from 'monkberry';
import { myParser } from './parser';

var compiler = new Compiler();
compiler.parsers.myParser = myTransform;

compiler.addSource('template', code, 'myParser');
compiler.addSource('another', code, 'monk');

var output = compiler.compile();

Unsafe

Monkberry escape all inserted variables by default. But if some times you want to insert some HTML template via variable you can you unsafe statement which is using innerHTML. Improper use of the unsafe statement can open you up to a cross-site scripting (XSS) attack.

{% unsafe '<a href="XSS">...</a>' %}
{% unsafe html %}

Comments

You can use standard html comments.

<!-- Comment does here -->

Comments will be cut out from template.

Blocks

Allows to define part of template as separate block:

{% block "partial" %}
  ...
{% endblock %}

This is very usefull then prerendering parts of templates, especially in loops.

{% for key, value of items %}
  {% block "item" %}
    ...   
  {% endblock %}
{% endfor %}

When to prerender loop block:

monkberry.prerender('item', 10); // Prerender item template 10 times.

API Reference

Monkberry API strictly follows semantic versioning.

Monkberry

Then using Monkberry via require('monkberry') single instance returned. To create a new Monkberry object or extend prototype use monkberry.constructor.

monkberry.render(name, [values, [noCache]])

Generates DOM nodes, and returns new Monkberry.View instance.

  • name: string - name of template.
  • values: Object - Optional. Data to update view.
  • noCache: boolean - Optional. Use or not cached view from pool.
monkberry.prerender(name, times)

Generates views for future calls of render method.

  • name: string - name of template.
  • times: number - how many times.
monkberry.mount(templates)

Add template to monkberry.

  • templates: Object - monkberry compiled templates.

Example:

monkberry.mount(require('./template.monk'));
monkberry.createPool()

Return new Monkberry instance.

monkberry.getPoolInfo()

Gets info about prerendered templates in pool/monkberry.

Monkberry.View

view.appendTo(toNode)

Append rendered view nodes to specified node.

  • toNode: Element - DOM node.
view.insertBefore(toNode)

Insert rendered view nodes before specified node.

  • toNode: Element - DOM node.
view.createDocument()

Return view's nodes. Note what if your template contains more then one root element, createDocument function will return DocumentFragment what contains all these nodes. If you have only one root node, it will be returned as is.

view.update(data)

Update rendered template with new data. You can specify only part of data to update or update entire data.

  • data: Object|Array - values to update in template.

Example:

var data = {
    title: 'Title #1',
    content: '...'
};

var view = monkberry.render('...', data);

view.update({title: 'Title #2'});
view.remove([force])

Remove view's nodes from document, and puts it to pool for future reuse.

  • force: boolean - Optional. False be default. If true, removed view will not be putted into pool.
view.querySelector(query)

Select node by query.

  • query: string - query to select node.

Note what this function uses Element.matches() for checking root nodes. Include polyfill for matches if you use it.

If you template contains more then one nodes on first level, querySelector will look other all subtrees. Array of all top level nodes can be accessed by view.nodes[] array.

Note what querySelector can not work with template which have if/for/custom node on first level.

{% if cond %}
    ...
{% endif %}

You will got exception like this: Can not use querySelector with non-element nodes on first level.

Solution is to wrap such statement into another node:

<div>
    {% if cond %}
        ...
    {% endif %}
</div>

Tests

Monkberry uses Jasmine and testem. To run test locally run:

testem ci

Plugins

Benchmarks

Benchmarks covers a few use cases, and compare Monkberry with React and temple-wat. Also it's contains real site code for soft/hard update tests.

Keywords

FAQs

Package last updated on 11 Jun 2016

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc