A Precompile and data-independent template engine for nodejs.
npm install comps --save
Using with ejs, compile and render before ejs rendering.
var comps = require('comps')
var ejs = require('ejs')
/**
* Custom method for loading component's template file by id
*/
comps.componentLoader(function (name) {
return fs.readFileSync(__dirname + '/c/' + name + '/' + name + '.tpl')
})
var tpl = comps({
template: '<div>{% component $id="header" /%}</div>'
})
var html = ejs.render(tpl, data)
See example.
- Param: options
<String>
, see options - Return:
<String>
Render given template to string directly with options.
Create Comps instance with isolated private variables.
var Comps = require('comps').create()
Comps({
// ...
})
- Param: name
<String>
- Param: value
Rendering configuration setter. Supporting properties:
- openTag
<String>
Template syntax of open delimiter. Default "{%". - closeTag
<String>
Template syntax of close delimiter.Default: "%}"
-
Param: name
<String>
Tag name, using as{% xxtag /%}
or{% xxtag %}{%/ xxtag %}
-
Param: def
<Object>
Tag configuration. Supporting properties:-
scope
<Boolean>
|<Function>
Optional Whether create a child-scope for the tag. -
paired
<Boolean>
Optional Restrains the type of tag. if true, can't not using tag as self-closing. If false, the tag must be self-closing. Otherwise has not constraint. -
created
<Function>
Call when tag is created. -
outer
<Function>
Call when tag is rendered. Must return Array with two items, item1 is the open tag, item2 is the close tag. -
inner
<Function>
Call when tag's child template is rendered. Must return String.
-
Context of defined method:
- $scope
<Object>
Scope of current context, properties will be herited to child-scope. - $el
<Object>
AST node of the tag. - $raw
<String>
Tag's raw content. - $name
<String>
Tag name. - $attributes
<Object>
All attributes of the tag. - $walk
<Function>
AST traverse method, using to continue traverse childNodes of the tag. - $render
<Function>
All attributes of the tag.
- Param: tpl
<String>
Compiled template as the same of options.template. - Return:
<Function>
Render method.
Pre-render template and return render method that receive options
as params.
- Param: options
<Object>
Template render options, see options - Return:
<Function>
Bigpipe factory function that will return bigpipe instance after calling
Pre-render template and return factory function that will be create a bigpipe
instance after calling.
- Param: options
<Object>
Template render options, see options - Return:
<Object>
Bigpipe instance. See Using Bigpipe
Create a bigpipe
instance directly.
- Param: loader
<Function>
Custom component template file loader method. loader
function will receive id<String>
as param, id is the component id that given by tag's $id attribute.
Loader should return object as result, and result must contains properties:request
, content
.
Note: Only one loader work, it will overwrite last loader.
- Param: loader
<Function>
Custom including template file loader method. loader
function will receive request<String>
and context<String>
as params. request is $path attribute and context is the current directory path of the request.
Loader should return object as result, and result must contains properties:request
, content
.
Note: Only one loader work, it will overwrite last loader.
- Param: transform
<Function>
Add call transform function before component tag rendering. this
of transform point to tag instance and receive component id<String>
as param.
Configuration:
var str = Comps({
template: '...',
pagelet: 'main.content'
})
Using pagelet in HTML template:
<div class="main">
{% pagelet $id="main" %}
<div class="content">
{% pagelet $id="content" %}
here is content.
{% /pagelet %}
out side.
</div>
{% /pagelet %}
</div>
Render result:
<div data-pageletid="main.content">
here is content.
</div>
It will be wrapped with a pagelet tag default, set $wrap to false
will disable wrapper.
Create bigpipe instance:
var creator = Comps.bcompile({
template: '...'
})
var bp = creator()
Using chunk in template:
<div>header-{{title}}</div>
{% chunk $id="header" $require="title" /%}
<ul>...{{list}}...</ul>
{% chunk $id="list" $require="list" /%}
<div class="footer"></footer>
Listen events and handle data:
bp.on('chunk', function (chunk) {
res.write(template.render(chunk, this.data))
})
bp.on('end', function () {
res.end()
})
// will emit "header" chunk
bp.set('title', 'xxx')
setTimeout(function () {
// will emit "list" chunk
bp.set('list', [..])
})
Using multiple data at once:
bp.set({
title: 'xxx',
list: []
})
Using endChunk() end up dependence waiting of the chunk:
bp.endChunk('header')
bp.endChunk('list')
Using flush() to check and write chunk when set data directly:
bp.data.title = ''
bp.data.list = []
bp.flush()
End bigpipe
manually, it flush remain chunks immediately but ignore waiting dependences:
bp.end()
Assume a component template ./index.tpl
as below:
<div class="index">
{% pagelet $id="list" %}
<ul class="list">
<li>item</li>
</ul>
{% /pagelet %}
</div>
If you need do client-side render and reuse the template in some case, you can use comps-loader .
Note: "comps-loader" is comps's template loader for webpack, and you need do some configuration when use it. See detail
Load template in client-side when using comps-loader:
// load full template file
var tpl = require('./index.tpl')
// load pagelet of template
var pagelet = require('!!comps!./index.tpl?pagelet=list')
Pagelet result =>
<ul class="list">
<li>item</li>
</ul>
If you don't like require('!!comps!./index.tpl?pagelet=list')
, you can create an independ file for list template:
./index.tpl
<div class="index">
{% include $path="./list.tpl" /%}
</div>
./list.tpl
:
<ul class="list">
<li>item</li>
</ul>
Load templates:
// load full template file
var tpl = require('./index.tpl')
// load list template
var pagelet = require('./index.tpl')
Options of rendering template with comps(options)
:
- Type:
<String>
HTML template for rendering.
- Type:
<String>
- Optional
Render those template including in pagelet tag. It compare pagelet
option with pagelet tag's $id
.
See Using pagelet.
- Type:
<Boolean>
- Optional
Note: Chunk is enable default in
bigpipe
rendering.
If chunk is true, Comps
will render Chunk tag to CHUNK_SPLITER
, such as: <!--{% chunk /%}-->
.
See Using Bigpipe.
- Type:
<String>
- Optional
Note: Using with
include
tag.
Specify current directory path of the given template.
All build-in available tag of comps.
Component tag is using to load and handle component template file
Example
{% component $id="header" /%}
It will call componentLoader
to loader component file by id "header".
Tag attributes:
- $id Id name of the component for load component file.
- $tag Specify tag name of component wrapper tag. Optional
- $replace Using component wrapper tag of not, default
false
. Set to "nomerge
" will not copy attributes to template container element, otherwise all attribute from the component tag will copy to template container element and overwrite exist attribute.Optional
Events
-
componentcreated(tagInstance)
-
beforecomponentload(id, tagInstance)
-
componentloaded(id, tagInstance, result)
After load, will get request/context of the component in "tagInstance", changing will change render result.
Insertion point
{% component $id="header" /%}Inner Content{% /component %}
Using $content
:
<div class="header">
{%> $content /%}
</div>
Will render to:
<div class="header">
Inner Content
</div>
noti: Comps's tags of
Inner Content
will be rendered by outer component scope
Pagelet tag is using to render template only that included in pagelet if pagelet
option is given.
Example:
{% pagelet $id="header" $wrap=false %}
<div class="header"></div>
{%/ pagelet %}
If pagelet of rendering options is "header", it will render the template included in pagelet tag only.
Attributes:
- $id Id name of the pagelet for matching.
- $tag Specify tag name of pagelet wrapper tag. Optional
- $wrap Using pagelet wrapper tag of not, default
false
. Optional
Inline another HTML template file into current template.
Example:
{% include $path="./header.tpl" /%}
Attributes:
- $path File path, can be relative or absolute.
Events
-
beforefileload(request, context, tagInstance)
-
fileloaded(result, tagInstance)
After load, will get request/context of the file in "tagInstance", changing will change render result.
Passing data
{% include $path="./header.tpl" $data="{title: 'Comps passing data from include'}"/%}
Using in header.tpl
<div class="header">{%> title /%}</div>
Will render to:
<div class="header">Comps passing data from include</div>
Bigpipe chunk split tag, and declare data dependences of above chunk.
Example:
...
<div class="header">...</div>
{% chunk $require="title,name" /%}
<div class="footer">...</div>
...
chunk
event will be emitted if require dependences are done.
Attributes:
- $require Require dependences, multiple keys splited by "
,
" .
Execute expression and output data that given by component.
Declare data in component tag:
{% component $id="main" $data="{ title: 'Output ag', content: 'Data from components' }" /%}
Templte of "main"
component:
<div>
{%> 'Title is: ' + title /%}
{%> 'Content is: ' + content /%}
</div>
Render result:
<div>
Title is: Output tag
Content is: Data from components
</div>
Build in properties and methods:
- $exist <
Function
> checkout the property in current scope exist or not - $get <
Function
> get property value from current scope - $data <
Object
> expose scope's data object
Branch logic, it will render contents if condition's value is truly.
{% component $id="main"
$data="{ isShowText: true }"
/%}
Templte of "main"
component:
<div>
{%? isShowText%}
Hello world
{%/?%}
</div>
Render result:
<div>
Hello world
</div>
Has same build in properties and methods of output(>)
Create data scope and declare variables.
{% repeat $items="['Comps','Repeat']" $as="item" $index="index" %}
Index: {%> index /%}, Item: {%> item /%}
{% /data %}
Render result:
Index: 0, Item: Comps
Index: 1, Item: Repeat
Create data scope and declare variables.
{% data name="send" %}
My name: {%> name /%}
{% /data %}
Render result:
My name: send