模板使用手册

概述

NEJ模板系统功能强大,提供了7大模板。NEJ模板系统整体架构图如下图所示。NEJ模板系统包含基本模板(Text模板、Node、Item模板、JST模板)和扩展模板(CSS模板、JS模板、HTML模板)。基本模板提供了通用的结构模板功能,扩展模板提供了延迟加载各种客户端资源(CSS、JS、HTML)的功能。Loader是一个资源加载引擎,扩展模板会使用,开发人员不会直接使用。Text、Node模板只提供了基本的缓存功能,开发人员一般也不会直接使用。在使用结构模板的时候一般会选用JST模板或者Item模板。

img

NEJ模板系统使用起来非常方便。除Item模板以外,其他模板都可以通过通用接口nej.e._$parseTemplate来添加或使用。nej.e._$parseTemplate接口文档如下。nej.e._$parseTemplate接口的应用实例,会在具体模板的范例里面说明。

img

NEJ模板系统非常注重效率。模板在添加的时候,不会解析。在初次使用的时候,模板才会被解析,并且将解析结果缓存起来以备后续使用。

基本模板

基本模板就是我们通常所说的客户端结构模板,包括Text模板、Node模板、JST模板、Item模板。这些模板提供了html、dom以及页面逻辑的缓存。 开发人员使用这些模板,可以在用户操作时,便利地展示页面结构(初始不可见的);可以在异步数据返回时,便利地拼装数据与页面结构,展示给用户。

text模板

ext模板提供了基本的文本缓存功能, 模板标示符为txt。开发人员可以使用这个模板来缓存html结构,但是一般情况下开发人员比较少使用Text模板,而是使用功能更强大的Item模板或JST模板。

nej.e._$addTextTemplate

img

nej.e._$getTextTemplate

img

范例

需求: 下图是网易博客中设置个人教育经历的部分视觉效果。

img

当用户曾上过多个高中时,可以通过点击“添加高中”添加一行,点击后的效果如下图所示。

img

下面是用Text模板实现以上需求的html、js代码:

HTML: 相关的主要html结构如下:

<!-- 高中模块 --> 
<div id="seniors">
    <div class="senior">
        <label>高中:</label>
        <select><option>-选择高中-</option></select>
        <select><option>年份</option></select>
        <span>-</span>
        <select><option>年份</option></select>
        <a href="javascript:void(0);">删除</a>
    </div>
</div>
<!-- /高中模块 -->

<!-- Text模板 -->
<textarea name="txt" id="seniorTextTemplate" style="display:none;">
    <div class="senior">
        <label>高中:</label>
        <select><option>-选择高中-</option></select>
        <select><option>年份</option></select>
        <span>-</span>
        <select><option>年份</option></select>
        <a href="javascript:void(0);">删除</a>
    </div>
</textarea>
<!-- /Text模板 -->

作为Text模板容器的textarea节点的name属性值必须为Text模板标示符txt。

JS: 主要逻辑代码如下:

<script>
// 解析模板
e._$parseTemplate('seniorTextTemplate');
…
// 点击“添加高中”的响应函数
function onClickAddSenior(){
    var _econtent = e._$get('seniors');
    var _html = e._$getTextTemplate('seniorTextTemplate');
    var _node = e._$html2node(_html);
    _econtent.appendChild(_node);
}
</script>

Node模板

Node模板提供了基本的节点缓存功能,模板标示符为ntp。开发人员可以使用这个模板来缓存DOM节点,但是一般情况下开发人员不会直接使用Node模板,而是使用Item模板。在使用Item模板时,使用Node模板来生成节点。

nej.e._$addNodeTemplate

img

应用实例1:

<script>
// 以下代码将id为'photoList'的节点添加到缓存中
var _key = e._$addNodeTemplate('photoList');
</script>

应用实例2:

<script>
// 以下代码将_element节点以节点id为键值后缀添加到缓存中
var _key = e._$addNodeTemplate(_element, _element.id);
</script>

nej.e._$getNodeTemplate

img

应用实例:

<script>
// 以下代码将从缓存中获取键值为_key的节点
var _node = e._$getNodeTemplate(_key);
</script>

范例

同addTextTemplate

Item模板

Item模板提供了(节点+逻辑)缓存功能,非常适合带有逻辑的列表项的开发。Item模板一般使用Node模板来获取节点。

nej.e._$getItemTemplate

img

应用实例:

<script>
// 以下代码将用_friends数据,用wwq._$$FriendItem构造出列表项
var _friends = [{name:'魏文庆', gender:1}, {name:'严跃杰', gender:1}, {name:'张晓容'}];
var _friendItems = e._$getItemTemplate(_friends, wwq._$$FriendItem);
</script>

范例

需求: 下图是管理好友模块的部分视觉效果。

img

下面是用Item模板实现以上需求的html、js代码:

HTML: 相关的主要html结构如下:

<!—名片列表模块 --> 
<div id="friends">
</div>
<!-- /名片列表模块 -->
…
<div id="templates" style="display:none;">
<!-- node模板 -->
<textarea id="friendNodeTemplate" name="ntp">
    <div class="m-friend">
        <img title="头像"/>
        <div class="info">
<span></span>.<span></span>,<span></span>
</div>
        <input type="checkbox"/>
<a href="javascript:void(0);" title="删除">×</a>
     </div>
</textarea>
<!-- /node模板 -->
<!-- 其他模板 -->
<!-- ....... -->
<!-- /其他模板 -->
</div>

JS: 主要逻辑代码如下:

<script>
// 解析Node模板
e._$parseTemplate('templates');
…
// 添加名片列表逻辑
var _friends = [{name:'魏文庆', gender:1, ava:'../image/0.jpg'},
                         {name:'严跃杰', gender:1, ava:'../image/1.jpg'}, 
                         {name:'张晓容', ava:'../image/2.jpg'}];
friendItems = e._$getItemTemplate(_friends, wwq._$$FriendItem, {
    parent: 'friends'  // 容器节点id
});
…
// 列表项类wwq._$$FriendItem实现逻辑
var wwq = NEJ.P('wwq');
var pro, supro;
// 好友项
wwq._$$FriendItem = NEJ.C();
pro = wwq._$$FriendItem._$extend(ui._$$Item, !0);
supro = ui._$$Item.prototype;
/**
 * 初始化节点,子类重写具体逻辑
 * @return {Void}
 */
pro.__initNode = function(){
    supro.__initNode.apply(this, arguments);
    var _espans = this.__body.getElementsByTagName('span');
    this.__ecb = this.__body.getElementsByTagName('input')[0];
    this.__eimg = this.__body.getElementsByTagName('img')[0];
    this.__enumber = _espans[0];
    this.__ename = _espans[1];
    this.__egender = _espans[2];
    this.__edelete = this.__body.getElementsByTagName('a')[0];
    v._$addEvent(this.__edelete, 'click', this.__onClickDelete._$bind(this));
    v._$addEvent(this.__ecb, 'click', this.__onClickCheck._$bind(this));
};
/**
 * 初始化外观信息
 * @return {Void}
 */
pro.__initXGui = function(){
    // 设置节点模板的id,将结构和逻辑关联起来
    this.__seed_html = 'friendNodeTemplate';
};
/**
 * 刷新项,子类实现具体逻辑
 * @return {Void}
 */
pro.__doRefresh = function(_data){
    this.__eimg.src = _data.ava;
    this.__enumber.innerText = this.__index + 1;
    this.__ename.innerText = _data.name;
    this.__egender.innerText = _data.gender?'男':'女';
};
/**
 * 点击删除的响应函数
 * @param {Object} _event   事件对象
 */
pro.__onClickDelete = function(_event){
    // todo
};
/**
 * 点击复选框的响应函数
 * @param {Object} _event   事件对象
 */
pro.__onClickCheck = function(_event){
    this.__ecb.checked?e._$addClassName(this.__body, 'checked'):e._$delClassName(this.__body, 'checked');;
};
/**
 * 控件销毁
 * @return {Void}
 */
pro.__destroy = function(){
    supro.__destroy.apply(this, arguments);
    this.__eimg.src = '';
    this.__ecb.checked = false;
    e._$delClassName(this.__body, 'checked');
};
</script>

JST模板

JST模板是基于模板生成文本输出的通用工具,模板标示符为jst。JST的语法类似于freemaker。开发人员一般使用JST来合并数据与html结构。

nej.e._$addHtmlTemplate

img

应用实例1:

<script>
// 以下代码将jst字符串_workersHtml添加到缓存中
var _workersHtml = '<table class="w-table">\
        <thead>\
            <tr><th>序号</th><th>姓名</th><th>性别</th></tr>\
        </thead>\
        {if !defined("workers")}\
            <tr><td colspan="3">数据加载失败,请稍后再试!</td></tr>\
        {elseif workers&&workers.length}\
            {list workers as x}\
                <tr{if x_index==x_length-1} class="last"{/if}>\
<td>${x_index+1}</td>\
<td>${x.name}</td>\
<td>{if x.gender==1}男{else}女{/if}</td>\
</tr>\
            {/list}\
        {else}\
            <tr><td colspan="3">没有工人!</td></tr>\
        {/if}\
    </table>'
var _key = e._$addHtmlTemplate(_workersHtml);
</script>

应用实例2: HTML:

<!-- jst模板 -->
<textarea id="workersJstTemplate" style="display:none;">
    <table class="w-table">
        <thead>
            <tr><th>序号</th><th>姓名</th><th>性别</th></tr>
        </thead>
        {if !defined("workers")}
            <tr><td colspan="3">数据加载失败,请稍后再试!</td></tr>
        {elseif workers&&workers.length}
            {list workers as x}
                <tr{if x_index==x_length-1} class="last"{/if}>
<td>${x_index+1}</td>
<td>${x.name}</td>
<td>{if x.gender==1}男{else}女{/if}</td>
</tr>
            {/list}
        {else}
            <tr><td colspan="3">没有工人!</td></tr>
        {/if}
    </table>
</textarea>
<!-- /jst模板 -->

JS:

<script>
// 以下代码将id为'workersJstTemplate'的文本域的值作为jst字符串添加到缓存中
var _key = e._$addHtmlTemplate('workersJstTemplate');
</script>

nej.e._$getHtmlTemplate

img

应用实例1:

<script>
// 以下代码将数据_data与键值为_key的jst模板合并
var _data = {workers: [{name:'魏文庆', gender:1}, 
                        {name:'严跃杰', gender:1}, 
                        {name:'张晓容'}]};
var _html = e._$getHtmlTemplate(_key, _data);
</script>

应用实例2:

<script>
// 以下代码将数据_data与键值为_key的jst模板合并
// 扩展接口传入formatGender,在键值为_key的jst模板可以使用该接口
// 比如:${_worker.gender|formatGender}
var _data = {workers: [{name:'魏文庆', gender:1}, 
                        {name:'严跃杰', gender:1}, 
                        {name:'张晓容'}]};
var _html = e._$getHtmlTemplate(_key, _data, {formatGender: function(_value){
    return _value==1?'男':'女';
}});
</script>

nej.e._$renderHtmlTemplate

img

应用实例:

<script>
// 以下代码将数据_data与键值为_key的jst模板合并
// 合并的结果设置为id为content的节点的html
var _data = {workers: [{name:'魏文庆', gender:1}, 
                        {name:'严跃杰', gender:1}, 
                        {name:'张晓容'}]};
var _html = e._$renderHtmlTemplate('content', _key, _data);
</script>

范例

需求: 下图是工人列表模块的部分视觉效果。

img

下面是用JST模板实现以上需求的html、js代码:

HTML: 相关的主要html结构如下:

<!-- 工人列表容器节点 -->
<div class="content">       
</div>
<!-- /工人列表容器节点 -->
…
<!-- jst模板 -->
<textarea id="workersJstTemplate" style="display:none;">
    <table class="w-table">
        <thead>
            <tr><th>序号</th><th>姓名</th><th>性别</th></tr>
        </thead>
        {if !defined("workers")}
            <tr><td colspan="3">数据加载失败,请稍后再试!</td></tr>
        {elseif workers&&workers.length}
            {list workers as x}
                <tr{if x_index==x_length-1} class="last"{/if}>
<td>${x_index+1}</td>
<td>${x.name}</td>
<td>{if x.gender==1}男{else}女{/if}</td>
</tr>
            {/list}
        {else}
            <tr><td colspan="3">没有工人!</td></tr>
        {/if}
    </table>
</textarea>
<!-- /jst模板 -->

JS: 主要逻辑代码如下:

<script>
// 解析JST模板
e._$parseTemplate('workersJstTemplate');
…
// 数据与模板合并,并且显示到页面上
var _data = {workers: [{name:'魏文庆', gender:1}, 
                        {name:'严跃杰', gender:1}, 
                        {name:'张晓容'}]};
e._$renderHtmlTemplate('content', 'workersJstTemplate', _data);
</script>

语法

JST的语法与freemarker类似,详情请参考JST语法参考。

扩展模板

扩展模板提供了资源(css、js、html)的延迟加载/生效功能。扩展模板都是通过通用接口nej.e._$parseTemplate来调用。

CSS模板

CSS模板提供了CSS文件延迟加载以及CSS代码段延迟生效功能, 模板标示符为css。CSS模板是通过通用接口nej.e._$parseTemplate来调用。

范例

需求: 在用户的某个操作后,a.css才会被加载,一段css代码段会生效。 下面是用CSS模板实现以上需求的html、js代码:

HTML: 相关的主要html结构如下:

<!-- CSS模板 -->
<textarea id="cssTemplate"  name="css" data-src="/css/a.css" style="display:none;">
    .g-doc{width:auto;}
.m-btns button{margin-right:10px;}
.w-table{margin-top:10px;width:100%;}
.w-table .hint{background-color:#ffe;height:50px; }
.w-table .last{background-color:#ffe;}
.content{height:450px;}
</textarea>
<!-- /CSS模板 -->

JS: 主要逻辑代码如下:

<script>
// 解析JS模板
// 以下代码执行后,/css/a.css加载并生效,textarea里的代码段也会生效
e._$parseTemplate('cssTemplate');
</script>

JS模板

JS模板提供了js文件延迟加载以及js代码段延迟生效功能, 模板标示符为js。JS模板是通过通用接口nej.e._$parseTemplate来调用。

范例

需求: 在用户的某个操作后,a.js、b.js才会被加载,一段js代码段会生效。 下面是用JS模板实现以上需求的html、js代码:

HTML: 相关的主要html结构如下:

<!-- JS模板 -->
<div id="jsTemplate" style="display:none;">
<textarea name="js" data-src="/js/a.js"></textarea>
<textarea name="js" data-src="/js/b.js">
    var f = function(){
            var e = NEJ.P('nej.e');
            e._$get('common').disabled = false;
            e._$get('empty').disabled = false;
            e._$get('failed').disabled = false;
            e._$get('string').disabled = false;
};
define(['{lib}base/element.js'], f);
</textarea>
</div>
<!-- /JS模板 -->

JS: 主要逻辑代码如下:

// 解析JS模板
// 以下代码执行后,/js/a.js、/js/b.js加载并生效,textarea里的代码段也会生效
e._$parseTemplate('jsTemplate');

HTML模板

CSS模板、JS模板提供了样式、逻辑的延迟加载功能,而在现实的开发当中开发人员更多的需求是延迟加载模块(模块结构、模块样式、模块的逻辑)。例如,用户使用网易邮箱时,从收件箱跳到网盘,如果使用延迟加载技术,这时就需要加载网盘模块的结构、样式、逻辑。 HTML模板提供了模块延迟加载的功能, 模板标示符为html。HTML模板是通过通用接口nej.e._$parseTemplate来调用。

范例

需求: 用户进入网易博客个人中心时,显示个人中心首页。用户点击左栏导航的“好友管理”,显示好友管理模块,如下图所示。

img

下面是用HTML模板实现好友管理模块延迟加载的html、js代码:

HTML: 相关的主要html结构如下:

<!-- html模板 -->
<textarea id="htmlTemplate" name="html" data-src="template.friends.html" style="display:none;"></textarea>
<!-- /html模板 -->
好友模块模板html文件template.friends.html内容如下:
<!-- ie6未指定编码默认为GBK,其他浏览器与父页面编码相同 -->
<meta charset="utf-8"/>
<!-- css模板 -->
<textarea name="css">
    #friends{margin-left:-20px;overflow:hidden;}
.m-friend{float:left;_display:inline;position:relative;zoom:1;
margin:20px;border:1px solid #eee;}
.m-friend.checked{border-color:red;}
.m-friend img{width:100px;height:100px;margin:3px;}
.m-friend .info{margin-left:3px;}
.m-friend input{position:absolute;left:3px;top:3px;}
.m-friend a{position:absolute;right:4px;top:-1px;line-height:1;
font-size:20px;text-decoration:none;}
</textarea>
<!-- /css模板 -->
<!-- node模板 -->
<textarea id="friendsNodeTemplate" name="ntp" style="display:none;">
    <div  data-module="friends">
    </div>
</textarea>
<!-- /node模板 -->
<!-- node模板 -->
<textarea id="friendNodeTemplate" name="ntp" style="display:none;">
    <div class="m-friend">
        <img title="头像"/>
        <div class="info">
<span></span>.<span></span>,<span></span>
</div>
        <input type="checkbox"/>
<a href="javascript:void(0);" title="删除">×</a>
     </div>
</textarea>
<!-- /node模板 -->
<!-- js模板 -->
<textarea name="js" data-src="../javascript/template.friends.js"></textarea>
<!-- /js模板 -->

JS: 主要逻辑代码如下:

<script>
// 延迟加载模块的逻辑
// 以下代码执行后,好友模块的模板template.1.html会被加载执行
e._$parseTemplate('htmlTemplate');
</script>

好友模块模板引用的js文件template.friends.js主要内容如下:

<script>
var wwq = NEJ.P('wwq');
// 页面初始化接口
function init(){
    e._$parseTemplate('friendsNodeTemplate');
    var _efriends = e._$getNodeTemplate('friendsNodeTemplate');
    e._$parseTemplate('friendNodeTemplate');
    var _friends = [{name:'魏文庆', gender:1, ava:'../image/0.jpg'},
                             {name:'严跃杰', gender:1, ava:'../image/1.jpg'}, 
                             {name:'张晓容', ava:'../image/2.jpg'}];
    e._$getItemTemplate(_friends, wwq._$$FriendItem, {
        parent: _efriends,
        key: 'friendNodeTemplate'
    });
    e._$get('main').appendChild(_efriends);
}
init();
</script>

JST语法参考

表达式

${}

描述:求值表达式,表达式中不可以包含 “{”或者“}”

语法:

${expr}
${expr|modifier}
${expr|modifier1|modifier2|...|modifierN}
${expr|modifier1:argExpr1_1}
${expr|modifier1:argExpr1_1,argExpr1_2,...,argExpr1_N}
${expr|modifier1:argExpr1_1|...|modifierN:argExprN_1,argExprN_2,...,argExprN_M}

范例:

${customer.firstName}
${customer.firstName|capitalize}
${customer.firstName|default:"no name"|capitalize}
${article.getCreationDate|default:new Date()|toCalendarControl:"YYYY.MM.DD",true,"creation Date"}
${(lastQuarter.calcRevenue() - fixedCosts) / 10000}

${% %}

描述:求值表达式,表达式中可以包含 “{”或者“}”

语法:

${% expr %}

范例:

${% emitLink("Solution and Products", {color: "red", blink: false}) %}

语句

list break

描述:遍历数组

语法1:

{list seq as varName}
{break}
{/list}

范例1:

{list ["aaa", "bbbb", "ccccc"] as x}
            ${x_index}/${x_length}:${x}<br/>
{/list}

注:x_index为内置变量,值为循环的索引值。 x_length为内置变量,值为列表长度, 上例中值为3。

语法2:

{list from..to as varName}
{/list}

注:循环时包含from和to值

范例2:

{list 2..10 as x}
    ${x_index}/${x_length}:${x}<br/>
{/list}

注:x_index为内置变量,值为循环的索引值。 x_length为内置变量,值为列表长度, 上例中值为9。

for forelse

描述:遍历HASH表

语法:

{for varName in hash}
{forelse}
{/for}

注:forelse 子语句为可选

范例:

{for x in {a:"aaa", b:"bbbb", c:"ccccc"}}
    ${x_key} - ${x}<br/>
{forelse}
    no pro
{/for}

注:x_key为内置变量,值为当前项的键值。

if elseif else

描述:条件控制语句

语法:

{if expr}
{elseif expr}
{else}
{/if}

注:elseif、else 子语句为可选

范例:

{if gender == 1}
    男
{elseif gender == 0}
    女
{else}
    春哥
{/if}

var

描述:变量定义

语法:

{var varName}
{var varName = expr}

范例:

{var test = "sssssss"}

macro

描述:宏定义

语法:

{macro macroName(arg1, arg2, ... argN)}
    ...body of the macro ...
{/list}

范例:

{macro htmlList(dataList, optionalListTyp)}
    {var listType = optionalListTyp != null ? optionalListTyp : "ul"}
    <${listType}>
        {for item in dataList}
            <li>${item}</li>
        {/if}
    </${listType}>
{/list}

调用宏:

${htmlList(["首页", "日志",“相册”, "关于我"])}

输出:

<ul>
    <li>首页</li>
    <li>日志</li>
    <li>相册</li>
    <li>关于我</li>
</ul>

cdata

描述:文本块,内容不做语法解析

语法:

{cdata}
    ...no parsed text ...
{/cdata}

{cdata EOF}
    ...no parsed text ...
EOF

范例:

{cdata}
    ${customer.firstName}${customer.lastName}
{/cdata}

{cdata END_OF_CDATA_SECTION}
    ${customer.firstName}${customer.lastName}
END_OF_CDATA_SECTION

输出:

${customer.firstName}${customer.lastName}

minify

描述:压缩文本内容,内容不做语法解析

语法:

{minify}
    ...multi-line text which will be stripped of line-breaks...
{/minify}

{minify EOF}
    ...multi-line text which will be stripped of line-breaks...
EOF

范例:

{minify}
    no parsed
    text
    and 
    merge
    one
    line
{/minify}

{minify EOF}
    no parsed
    text
    and 
    merge
    one
    line
EOF

输出:

no parsed text and merge one line

eval

描述:执行javascript语句,不做语法解析

语法:

{eval}
    ...javascript statement...
{/eval}

{eval EOF}
    ...javascript statement...
EOF

范例:

{eval}
    var a = "aaaa";
alert(a);
function b(arg){
    alert(arg);
}
{/eval}

{eval EOF}
    var a = "aaaa";
alert(a);
function b(arg){
    alert(arg);
}
EOF

扩展

rand

描述:随机一个指定长度的纯数字的串

语法:

${number_expr|rand}

范例:

${10|rand}

输出:

3456785438

escape

描述:编码字符串

语法:

${expr|escape}

范例:

${"<div>1234<a href="#">163</a></div>"|escape}

输出:

&lt;div&gt;1234&lt;a href="#"&gt;163&lt;/a&gt;&lt;/div&gt;

format

描述:格式化日期

语法:

${data_expr|format:format_expr}

范例:

${new Date()|format:"yyyy-MM-dd HH:mm:ss"}

输出:

2012-06-13 16:30:55

default

描述:指定默认值

语法:

${expr|default:default_expr}

范例:

${null|default:"default value"}

输出:

default value
© 1997-2013 Netease. All Rights Reserved.
comments powered by Disqus