ocTemplate 是一个为“面向二次发开项目”而设计的 Node.js 模板引擎。
ocTemplate 允许开发者使用 jQuery 处理模板结构,除此以外 ocTemplate 和其他的模板引擎没有太大的区别。 例如你正在开发一个blog程序而且采用了 ocTemplate 做为模板引擎,当有人基于你的blog程序进行二次开发时(他们使用你的blog程序,但是想改些地方), 可以不必修改你的模板源代码,而是在ocTemplate渲染一个模板前加载这个模板,用 jQuery 修改模板上的内容,就像在浏览器里操作网页元素一样。 这些操作都在后端 Nodejs 里实现。
你可以在模板上使用这些jQuery函数来控制模板的内容和结构:$.fn.append()/prepend()/after()/before()/attr()/addClass()/removeClass() ... ...
在二次开发时避免修改原来程序的源代码,有两个好处:
-
原来程序在发布新版本时,不会出现源代码冲突。
-
“补丁”代码可以作为独立的项目分发。
ocTemplate 依赖jsdom,并且对 jsdom 做了修改,解除了 jsdom 对 contextify C++ 的依赖,使 ocTemplate 能够在纯JavaScript环境中工作。
ocTemplate 没有像 jade 那样简化 html 语法,jade的模板语法简洁、干静,没有多余的字符,非常适合“敏捷”开发,但是无法和 jQuery 兼容。所以 ocTemplate 仍然保持完整的 html 语法。
npm install octemplate
hello.js :
var tplCache = require("octemplate") ;
tplCache.template(__dirname+"/templates/hello.html",function(err,tpl){
tpl.render(
// data model
{
foo: 'bar'
}
// render callback
, function(err,buff){
console.log(''+buff) ;
}
) ;
}) ;templates/hello.html :
<div>
hello, world.
</div>
<div>
Value of the variable foo is: {@foo}
</div>mkdir node_modules
cd node_modules
git clone git@github.com:OpenComb/ocTemplate.git octemplate
cd octemplate
git checkout 0.1.3
模板表达式的基本形式是:{@ expression },它可以用于文本和标签属性中。
<div>
Value of the variable foo is: {@foo}
</div><div {@foo}="123"></div>渲染后的结果是:
<div bar="123"></div><div foo="value of variable foo is: {@bar}"></div><div foo="@bar"></div>所有以@开头的属性值,将被视做一个表达式,计算后的结果以字符串的形式输出。
注意:有些模板标签的属性必须是表达式才有意义,漏掉
@模板引擎不会报告错误,但逻辑并不正确,这是一个常见的新手错误。例如:
<if condition="foo">Variable foo is avalid.</if>这是错误的,condition 会收到一个字符串:"foo" 而不是变量 foo,因此判断总是为 true 。
<!-- 不能漏掉那个 @ -->
<if condition="@foo">Variable foo is avalid.</if>ocTemplate 仅支持单行表达式,如果提供多行表达式,只会执行第一行并返回其结果。
result of expression: {@ var i=123; return i } .渲染结果:
result of expression: undefined .这不是预期的效果。这种情况下,可以使用匿名函数来包装多行表达式:
result of expression: {@ (function(){ var i=123; return i ;})() } .渲染结果:
result of expression: 123 .用 (function(){ ... })() 模式将多行表达式包装起来即可。
预置变量$model是模板中所有变量的名称空间。
tpl.render() 的参数 model 中的内容都在 $model 中;所有$model对象的属性,都可以在模板表达式中作为独立的变量使用。
例如:
<div> {@foo} </div>或者:
<div> {@$model.foo} </div>对于变量foo,{@foo} 和 {@$model.foo} 这两种写法:
-
如果
foo变量存在,两者具有效果相同; -
如果
foo变量不存在,则{@foo}抛出异常(会打断后文的模板内容渲染),{@$model.foo}则输出undefined。
简单的说,和JavaScript的标准行为一致。
因此如果不能保证 foo 变量一定存在,使用 {@$model.foo} 代替 {@foo}
{@$model.foo}的效果类似于:
```html
<div>
{@ typeof foo!='undefined'? foo: undefined}
</div>
```
属性:
- [必须]
condition: 条件表达式
example:
<if condition="@foo=='bar'">
{@foo}
</if>注意:不要漏掉 condition 属性里的
@,否则会被当作一个字符串,那多半不是你想要的效果。
属性:
-
[必须]
for: 循环目标对象表达式,表达式返回的结果可以是 Object 或 Array 。 -
[可选]
var: 迭代变量名称,创建对应变量保存每一轮迭代出来的值。var需要的是一个字符串作为变量名称,如果提供表达式,则表达式的结果会被转换成字符串传递给var(这可能不是你想要的)。 -
[可选]
key: 迭代键或下标变量名称。提供给
for属性的如果是一个 Object,则key对应名称的变量中存入的是Object的属性名;如果提供Array类型给for,则为数组下标。
example:
<foreach for="@foo" key="key" var="item">
attribute {@key} of variable foo is: {@item}
</foreach>注意:只有for属性是一个表达式(
@开头),key和var只是简单是字符型属性值,它们作为变量名称
这是一个简化的 for 循环。
<loop> 仅支持针对整数的迭代
属性:
-
[必须]
end: 循环结束数值(包含end值)。 -
[可选]
start: 循环开始数值。默认为:1。 -
[可选]
step: 循环步长。默认为:1。 -
[可选]
var: 迭代变量名称,创建对应变量保存每一轮迭代出来的值。
这些属性可以是普通属性值或表达式,无论什么类型都会被转换成整数。
example:
foo = 7
<loop end="@foo" step="2" start="2" var="value">
{@ value}
</loop>结果:
2
4
6
在所有流程控制标签中提供 else if 效果;支持: <if>, <loop>, <foreach> 等
属性:
- [必须]
condition, 参看 <if> 同名属性
在所有流程控制标签中提供 else 效果;支持: <if>, <loop>, <foreach> 等
无属性
在所有流程控制标签中提供 continue 效果;支持: <if>, <loop>, <foreach> 等
无属性
在所有流程控制标签中提供 break 效果;支持: <if>, <loop>, <foreach> 等
无属性
引用另一个模板。
属性:
-
[必须]
file: 引用模板的文件路径,可以是表达式或普通文本属性。路径查找规则和 Nodejs 相同;如果提供给file的是一个相对路径,则相对当前模板的位置。
-
[可选]
model: 传递给被引用模板的变量模型。如果希望将当前模板的所有变量都传给被应用模板,可以这样:
model="@$model"。$model是当前模板的名称空间。
某些较复杂的逻辑或表达式不适合出现在模板,我们将他们封装到了 $helper 对象中。
$helper 是一个对象,该对象提供了一组状态无关的方法,用来简化模板表达式的逻辑,预置了下列方法:
-
addslashes(text)
为
\,",'以及 0字节 添加转义斜线。 -
eq (lft,rgt)
如果 lft 和 rgt 相等(==),返回true,否则返回false。
-
gt (lft,rgt)
如果 lft 大于 rgt(>),返回true,否则返回false。
-
lt (lft,rgt)
如果 lft 小于 rgt(<),返回true,否则返回false。
-
ge (lft,rgt)
如果 lft 大于等于(>=) rgt,返回true,否则返回false。
-
le (lft,rgt)
如果 lft 小于等于(<=) rgt,返回true,否则返回false。
比较运算符中的
<和>与模板中的标签边界符号相冲突,所以必须使用 $helper 中的比较方法(compare methods)。
example:
<if condition="@ $helper.gl(foo,bar)">
variable foo is greater then variable bar .
<else>
variable foo is not greater then variable bar .
</if>以上都是内容和其他模板引擎没有太大区别,接下来才是 ocTemplate 的重点。
ocTemplate 支持 jQuery, 每一个模板对象都可以被当作一个独立的 browser 环境,jQuery可以在模板对象(template)内运行,其行为和在浏览器里完全一致。
var tplCache = require("octemplate") ;
// 加载 template
tplCache.template(__dirname+"/templates/hello.html",function(err,tpl){
// 处理模板加载错误 err
// todo ...
// 在导航菜单中加入一个菜单项
tpl.$("ul.navMenu").append("<li><a href='javascript:alert(\"hello world.\")'>hello</a></li>") ;
// 重新编译模板
tpl.compile() ;
}) ;也可以在向模板中添加流程控制标签和表达式 (Cool!):
// ...
// 在导航菜单中加入一个菜单项
// (使用 <if> 判断是否存在变量 username,并将 username 里的值输出到 alert 里 )
tpl.$("ul.navMenu").append(
"<if condition='@$model.username'>"
+ "<li>"
"<a href='javascript:alert(\"hello, {@$model.username}\"+)'>hello</a>"
+ "</li>
+ "</if>"
) ;
// ...在 ocTemplate 的模板中执行事件相关的jQuery操作没有意义:有些事件可能不会触发,即使触发也不会发生在浏览器里,与用户交互无关。
一个错误的例子:
tpl.$("ul li a.login").click(function(){
// 永远不会触发的事件
alert("you want login?") ;
}) ; 上面的用法实际上是混淆的前端和后端的区别,ocTempate 对jQuery 的支持,仅仅用于控制模板文件里的内容,不直接支持网页的前端行为。