首页 > 代码库 > js模版引擎handlebars.js实用教程
js模版引擎handlebars.js实用教程
一、为什么选择Handlebars.js
据小菜了解,对于java开发,涉及到页面展示时,比较主流的有两种解决方案:
1. struts2+vo+el表达式。
这种方式,重点不在于struts2,而是vo和el表达式,其基本思想是:根据页面需要的信息,构造出一个实体,这个实体中包含了界面需要的所有属性,通常这个实体是由N个表中的字段构成的,俗称vo。由于vo的属性可以是String、List、Map等等等,又可以vo套vo,因此这种方式非常灵活,也非常好用。
在后台对vo进行赋值,通过struts2封装到request中,然后在界面用el表达式,通常是${}、forEach标签什么的,即可构造出界面。
但这种方式小菜却不是很看好。因为这种利用标签控制html,依然是把表现和控制混杂在一起,html里边混杂了大量el控制标签,很不美观。
当然,小菜不可能因为这么简单的理由拒绝这种方式,读者仔细思考可以发现,利用el表达式生成html代码,这是一个在服务器端执行的动作,在服务器端解析完成之后,才发送到客户端浏览器上,这样做会占用大量服务器资源,而且速度缓慢。
示例代码:
1 <c:forEach var=‘bm‘ items="${contractAuditVo.borrowerModels}"> 2 <table> 3 <tbody> 4 <tr> 5 <td>借款人编号:</td> 6 <td> 7 <p> 8 ${bm.borrowerId } 9 </p>10 </td>11 </tr>12 <tr>13 <td>客户编号:</td>14 <td>15 <p>16 ${bm.customerId }17 </p>18 </td>19 </tr>20 <tr>21 <td>曾用名:</td>22 <td>23 <p>24 ${bm.usedName }25 </p>26 </td>27 </tr>28 </tbody>29 </table>30 </c:forEach>
2. Json+ajax+拼html。
这种方法一般是基于ajax请求,要求服务器端返回一个json类型的json字符串,这个json串中包含了界面所需的所有信息,界面拿到json串后,构造出html,完成界面展示。
小菜推荐这种方法,通过这种方式编写的页面,反应速度非常快,用户体验非常好。
因为服务器端只需要提供一个json串,由客户端完成解析,因此服务器承受的压力很小,目前的电脑配置都较高,客户端的浏览器解析js脚本很快,因此页面体验效果好。
解析的过程大致是通过Jquery的each方法,进行遍历。
但是小菜利用这种方式时,犯了一个致命的错误,小菜是通过原始的拼接html的方式,页面中写了大量html+=”<div>”;,这种写法使页面变得非常凌乱,几乎不可以维护。
示例代码:
1 var contractTextHtml=""; 2 $.each(jsonObject.cl,function(i,n){ 3 4 contractTextHtml=""; 5 6 //插入合同文本数据 7 contractTextHtml+="<div title=‘出借人信息---"+hiddenNull(n.cm.lenderName)+"‘ style=‘overflow:auto;padding:5px;‘>"; 8 contractTextHtml+="<table class=‘ui-table ui-table-noborder‘>"; 9 contractTextHtml+="<tbody>";10 contractTextHtml+="<tr><td>合同编号:</td><td><p>"+hiddenNull(n.cm.contractId)+"</p></td></tr>";11 contractTextHtml+="<tr class=‘ui-table-split‘><td>出借人姓名:</td><td><p>"+hiddenNull(n.cm.lenderName)+"</p></td></tr>";12 contractTextHtml+="<tr><td>出借人证件类型:</td><td><p>"+hiddenNull(n.cm.lenderIdType)+"</p></td></tr>";13 contractTextHtml+="<tr class=‘ui-table-split‘><td>出借人证件号:</td><td><p>"+hiddenNull(n.cm.lenderIdNum)+"</p></td></tr>";14 contractTextHtml+="<tr><td>出借金额:</td><td><p>"+hiddenNull(n.cm.lenderAmount)+"</p></td></tr>";15 contractTextHtml+="<tr class=‘ui-table-split‘><td>出借人编号:</td><td><p>"+hiddenNull(n.cm.lenderNo)+"</p></td></tr>";16 contractTextHtml+="<tr><td>出借人银行帐号:</td><td><p>"+hiddenNull(n.cm.lenderBankAccount)+"</p></td></tr>";17 contractTextHtml+="<tr class=‘ui-table-split‘><td>撮合编号:</td><td><p>"+hiddenNull(n.cm.makeMatchNo)+"</p></td></tr>";18 contractTextHtml+="</tbody>";19 contractTextHtml+="</table>";20 contractTextHtml+="</div>";21 $("#textList").append(contractTextHtml);22 });
造成这种问题的根本原因在于拼接html打乱了html原有的层次结构,看不出来哪里是哪里,没有了层次结构的代码,堆在那里就像是一坨垃圾。
el表达式构造html优点是能够保持html原有格式,js构造html优点是速度快省资源,为什么我们不能把二者的优点结合在一起呢?这就是Handlebars.js。
既然要在项目中引入js模版引擎,就必须进行技术选型,严格考核之后,才可以引入,就好像是木桶效应,不能让他成为项目中的短板。
Handlebars.js是一款基于Jquery的插件,以json对象为数据源,支持逻辑判断、循环等操作,同时具有非常好的扩展性,体积60KB左右,经过小菜仔细的分析研究,这是一款不可多得的js模版引擎。
二、如何引入Handlebars.js
Jquery插件,第一步当然要引用Jquery啦,然后引用Handlebars.js即可,仅仅需要这两个js文件。
1 <script type="text/javascript" src=http://www.mamicode.com/"script/jquery.js"></script>2 <script type="text/javascript" src=http://www.mamicode.com/"script/handlebars-1.0.0.beta.6.js"></script>
三、each-基本循环使用方法。
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <META http-equiv=Content-Type content="text/html; charset=utf-8"> 5 <title>each-基本循环使用方法 - by 杨元</title> 6 </head> 7 <body> 8 <h1>each-基本循环使用方法</h1> 9 <!--基础html框架-->10 <table>11 <thead>12 <tr>13 <th>姓名</th>14 <th>性别</th>15 <th>年龄</th>16 </tr>17 </thead>18 <tbody id="tableList">19 20 </tbody>21 </table>22 23 <!--插件引用-->24 <script type="text/javascript" src=http://www.mamicode.com/"script/jquery.js"></script>25 <script type="text/javascript" src=http://www.mamicode.com/"script/handlebars-1.0.0.beta.6.js"></script>26 27 <!--Handlebars.js模版-->28 <!--Handlebars.js模版放在script标签中,保留了html原有层次结构,模版中要写一些操作语句-->29 <!--id可以用来唯一确定一个模版,type是模版固定的写法-->30 <script id="table-template" type="text/x-handlebars-template">31 {{#each student}}32 <tr>33 <td>{{name}}</td>34 <td>{{sex}}</td>35 <td>{{age}}</td>36 </tr> 37 {{/each}}38 </script>39 40 <!--进行数据处理、html构造-->41 <script type="text/javascript">42 $(document).ready(function() {43 //模拟的json对象44 var data =http://www.mamicode.com/ {45 "student": [46 {47 "name": "张三",48 "sex": "0",49 "age": 1850 },51 {52 "name": "李四",53 "sex": "0",54 "age": 2255 },56 {57 "name": "妞妞",58 "sex": "1",59 "age": 1860 }61 ]62 };63 64 //注册一个Handlebars模版,通过id找到某一个模版,获取模版的html框架65 //$("#table-template").html()是jquery的语法,不懂的童鞋请恶补。。。66 var myTemplate = Handlebars.compile($("#table-template").html());67 68 //将json对象用刚刚注册的Handlebars模版封装,得到最终的html,插入到基础table中。69 $(‘#tableList‘).html(myTemplate(data));70 });71 </script>72 </body>73 </html>
通过阅读本例,读者已经掌握Handlebars.js一个重要特性:循环。#each可以理解成循环命令,循环的是json对象中的student属性。对于每次循环,都可以读出里边的name、sex、age属性。
四、each-循环中使用this
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <META http-equiv=Content-Type content="text/html; charset=utf-8"> 5 <title>each-循环中使用this - by 杨元</title> 6 </head> 7 <body> 8 <h1>each-循环中使用this</h1> 9 <!--基础html框架-->10 <table>11 <thead>12 <tr>13 <th>姓名</th>14 <th>性别</th>15 <th>年龄</th>16 </tr>17 </thead>18 <tbody id="tableList">19 20 </tbody>21 </table>22 23 <!--插件引用-->24 <script type="text/javascript" src=http://www.mamicode.com/"script/jquery.js"></script>25 <script type="text/javascript" src=http://www.mamicode.com/"script/handlebars-1.0.0.beta.6.js"></script>26 27 <!--Handlebars.js模版-->28 <!--Handlebars.js模版放在script标签中,保留了html原有层次结构,模版中要写一些操作语句-->29 <!--id可以用来唯一确定一个模版,type是模版固定的写法-->30 <script id="table-template" type="text/x-handlebars-template">31 {{#each this}}32 <tr>33 <td>{{name}}</td>34 <td>{{sex}}</td>35 <td>{{age}}</td>36 </tr> 37 {{/each}}38 </script>39 40 <!--进行数据处理、html构造-->41 <script type="text/javascript">42 $(document).ready(function() {43 //模拟的json对象44 var data =http://www.mamicode.com/ [45 {46 "name": "张三",47 "sex": "0",48 "age": 1849 },50 {51 "name": "李四",52 "sex": "0",53 "age": 2254 },55 {56 "name": "妞妞",57 "sex": "1",58 "age": 1859 }60 ];61 62 //注册一个Handlebars模版,通过id找到某一个模版,获取模版的html框架63 //$("#table-template").html()是jquery的语法,不懂的童鞋请恶补。。。64 var myTemplate = Handlebars.compile($("#table-template").html());65 66 //将json对象用刚刚注册的Handlebars模版封装,得到最终的html,插入到基础table中。67 $(‘#tableList‘).html(myTemplate(data));68 });69 </script>70 </body>71 </html>
很多时候,我们拿到的json对象,本身就是一个list,并不是map,直接就可以遍历,不需要#each student这样指定遍历某个属性。
此时可以用#each this,表示遍历当前对象。
this表示当前上下文,非常灵活,以后还会提及,读者可以举一反三。
五、each嵌套
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <META http-equiv=Content-Type content="text/html; charset=utf-8"> 5 <title>each嵌套 - by 杨元</title> 6 </head> 7 <body> 8 <h1>each嵌套</h1> 9 <!--基础html框架-->10 <div id="dataList"></div>11 12 <!--插件引用-->13 <script type="text/javascript" src=http://www.mamicode.com/"script/jquery.js"></script>14 <script type="text/javascript" src=http://www.mamicode.com/"script/handlebars-1.0.0.beta.6.js"></script>15 16 <!--Handlebars.js模版-->17 <!--Handlebars.js模版放在script标签中,保留了html原有层次结构,模版中要写一些操作语句-->18 <!--id可以用来唯一确定一个模版,type是模版固定的写法-->19 <script id="table-template" type="text/x-handlebars-template">20 {{#each this}}21 {{#each info}}22 {{../name}}的{{this}}<br>23 {{/each}}24 {{/each}}25 </script>26 27 <!--进行数据处理、html构造-->28 <script type="text/javascript">29 $(document).ready(function() {30 //模拟的json对象31 var data =http://www.mamicode.com/ [{32 "name":"张三",33 "info":[34 "眼睛",35 "耳朵",36 "鼻子"37 ]38 },{39 "name":"李四",40 "info":[41 "爸爸",42 "妈妈",43 "妻子"44 ]45 }];46 47 //注册一个Handlebars模版,通过id找到某一个模版,获取模版的html框架48 //$("#table-template").html()是jquery的语法,不懂的童鞋请恶补。。。49 var myTemplate = Handlebars.compile($("#table-template").html());50 51 //将json对象用刚刚注册的Handlebars模版封装,得到最终的html,插入到基础table中。52 $(‘#dataList‘).html(myTemplate(data));53 });54 </script>55 </body>56 </html>
循环嵌套在稍微复杂点的页面设计中即会涉及,Handlebars.js完全支持each嵌套,可以在each中使用each。
上边的例子演示了两个关键点:each嵌套的可实现性、如何在each嵌套中读取父each中的数据。
例子很简单,info本身是一个信息列表,是属于某个人的,我们先用each遍历所有的人,然后再遍历每个人的info信息,这样就形成了each嵌套。但是我们想在内层each中获取外层each的数据,达到“谁的什么”这样的显示效果。
显然,如果直接在内层each中使用{{name}},是取不到任何数据的,因为内层each的上下文是info,而name属性在表示人的上下文中。
此时,可以用{{../name}}从内层each获取上一级each的name属性,语法和html的路径表示一样,简单吧?
六、循环中使用索引
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <META http-equiv=Content-Type content="text/html; charset=utf-8"> 5 <title>关于循环中索引的使用 - by 杨元</title> 6 </head> 7 <body> 8 <h1>关于循环中索引的使</h1> 9 <!--基础html框架-->10 <table>11 <thead>12 <tr>13 <th>序号</th>14 <th>姓名</th>15 <th>性别</th>16 <th>年龄</th>17 </tr>18 </thead>19 <tbody id="tableList">20 21 </tbody>22 </table>23 24 <!--插件引用-->25 <script type="text/javascript" src=http://www.mamicode.com/"script/jquery.js"></script>26 <!--注意!这个例子用的是新版本handlebars,旧版本不支持-->27 <script type="text/javascript" src=http://www.mamicode.com/"script/handlebars-v1.3.0.js"></script>28 29 <!--Handlebars.js模版-->30 <!--Handlebars.js模版放在script标签中,保留了html原有层次结构,模版中要写一些操作语句-->31 <!--id可以用来唯一确定一个模版,type是模版固定的写法-->32 <script id="table-template" type="text/x-handlebars-template">33 {{#each student}}34 <tr>35 <td>{{addOne @index}}</td>36 <td>{{name}}</td>37 <td>{{sex}}</td>38 <td>{{age}}</td>39 </tr>40 {{/each}}41 </script>42 43 <!--进行数据处理、html构造-->44 <script type="text/javascript">45 $(document).ready(function() {46 //模拟的json对象47 var data =http://www.mamicode.com/ {48 "student": [49 {50 "name": "张三",51 "sex": "0",52 "age": 1853 },54 {55 "name": "李四",56 "sex": "0",57 "age": 2258 },59 {60 "name": "妞妞",61 "sex": "1",62 "age": 1963 }64 ]65 };66 67 //注册一个Handlebars模版,通过id找到某一个模版,获取模版的html框架68 //$("#table-template").html()是jquery的语法,不懂的童鞋请恶补。。。69 var myTemplate = Handlebars.compile($("#table-template").html());70 71 //注册一个Handlebars Helper,用来将索引+1,因为默认是从0开始的72 Handlebars.registerHelper("addOne",function(index,options){73 return parseInt(index)+1;74 });75 76 //将json对象用刚刚注册的Handlebars模版封装,得到最终的html,插入到基础table中。77 $(‘#tableList‘).html(myTemplate(data));78 });79 </script>80 </body>81 </html>
序号是指:在循环过程中,当前循环的索引,说白了,就是当前为第几次循环,一般从0开始,Handlebars.js中也是如此。
有什么用呢?最常见的是在显示table的时候,给每行加一个序号,清晰的展示出页面上共有多少条记录,如上边的例子。
除此之外,小菜为了在循环中区分不同的id,恰好需要一个序号,确保id不重复,用过bootstrap的童鞋应该知道,好多事件可以通过id定位,例如Listgroup控件。
废话少说,在Handlebars.js中,可以通过{{@index}}来获取当前的索引,也就是说@index这个变量就代表了当前索引。
在上边的例子里,table的序号从0开始不太好,于是注册了一个Helper,将索引+1。
七、Handlebars.js循环中索引(@index)使用技巧(访问父级索引)
使用Handlebars.js过程中,难免会使用循环,比如构造数据表格。而使用循环,又经常会用到索引,也就是获取当前循环到第几次了,一般会以这个为序号显示在页面上。
Handlebars.js中获取循环索引很简单,只需在循环中使用{{@index}}即可。
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <META http-equiv=Content-Type content="text/html; charset=utf-8"> 5 <title>Handlebars.js循环中索引(@index)使用技巧 - by 杨元</title> 6 </head> 7 <body> 8 <h1>Handlebars.js循环中索引(@index)使用技巧</h1> 9 <!--基础html框架-->10 <table>11 <thead>12 <tr>13 <th></th>14 <th>姓名</th>15 <th>性别</th>16 <th>年龄</th>17 </tr>18 </thead>19 <tbody id="tableList">20 21 </tbody>22 </table>23 24 <!--插件引用-->25 <script type="text/javascript" src="script/jquery.js"></script>26 <script type="text/javascript" src="script/handlebars-v1.3.0.js"></script>27 28 <!--Handlebars.js模版-->29 <!--Handlebars.js模版放在script标签中,保留了html原有层次结构,模版中要写一些操作语句-->30 <!--id可以用来唯一确定一个模版,type是模版固定的写法-->31 <script id="table-template" type="text/x-handlebars-template">32 {{#each this}}33 <tr>34 <td>{{@index}}.</td>35 <td>{{name}}</td>36 <td>{{sex}}</td>37 <td>{{age}}</td>38 </tr>39 {{/each}}40 </script>41 42 <!--进行数据处理、html构造-->43 <script type="text/javascript">44 var data = [{45 name: "张三",46 sex: "男",47 age: 3548 },{49 name: "李四",50 sex: "男",51 age: 2352 },{53 name: "甜妞",54 sex: "女",55 age: 1856 }];57 58 //解析模版59 var handle = Handlebars.compile($("#table-template").html());60 //生成html61 var html = handle(data);62 //插入到页面63 $("#tableList").append(html);64 65 </script>66 </body>67 </html>
虽然用{{@index}}可以获取到索引,但遗憾的是,索引是从0开始的。。。这样让人看起来很不舒服。因此,我们可以使用一个Helper来让索引+1,变成从1开始。
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <META http-equiv=Content-Type content="text/html; charset=utf-8"> 5 <title>Handlebars.js循环中索引(@index)使用技巧 - by 杨元</title> 6 </head> 7 <body> 8 <h1>Handlebars.js循环中索引(@index)使用技巧</h1> 9 <!--基础html框架-->10 <table>11 <thead>12 <tr>13 <th></th>14 <th>姓名</th>15 <th>性别</th>16 <th>年龄</th>17 </tr>18 </thead>19 <tbody id="tableList">20 21 </tbody>22 </table>23 24 <!--插件引用-->25 <script type="text/javascript" src="script/jquery.js"></script>26 <script type="text/javascript" src="script/handlebars-v1.3.0.js"></script>27 28 <!--Handlebars.js模版-->29 <!--Handlebars.js模版放在script标签中,保留了html原有层次结构,模版中要写一些操作语句-->30 <!--id可以用来唯一确定一个模版,type是模版固定的写法-->31 <script id="table-template" type="text/x-handlebars-template">32 {{#each this}}33 <tr>34 <td>{{addOne @index}}.</td>35 <td>{{name}}</td>36 <td>{{sex}}</td>37 <td>{{age}}</td>38 </tr>39 {{/each}}40 </script>41 42 <!--进行数据处理、html构造-->43 <script type="text/javascript">44 var data = [{45 name: "张三",46 sex: "男",47 age: 3548 },{49 name: "李四",50 sex: "男",51 age: 2352 },{53 name: "甜妞",54 sex: "女",55 age: 1856 }];57 58 //注册索引+1的helper59 var handleHelper = Handlebars.registerHelper("addOne",function(index){60 //返回+1之后的结果61 return index+1;62 });63 //解析模版64 var handle = Handlebars.compile($("#table-template").html());65 //生成html66 var html = handle(data);67 //插入到页面68 $("#tableList").append(html);69 70 </script>71 </body>72 </html>
不要以为这就完事了,这只是一般情况,小菜还要介绍一个非常特殊的场景。
假如循环中套了一个循环,在内层循环中如何获取外层循环的索引呢?
我们已经知道,可以用类似{{../name}}这种语法访问父级循环中的属性,但是这样访问父级索引是不可以的,例如:{{../@index}},这样写是不正确的。
我们可以通过一个小手段来获取。
写例子之前,小菜多废话几句,其实这个非常实用,可以创建分级的索引,比如1.1、1.2等,也可以用作特殊的标识,理解成分组。
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <META http-equiv=Content-Type content="text/html; charset=utf-8"> 5 <title>Handlebars.js循环中索引(@index)使用技巧 - by 杨元</title> 6 </head> 7 <body> 8 <h1>Handlebars.js循环中索引(@index)使用技巧</h1> 9 <!--基础html框架--> 10 <table> 11 <thead> 12 <tr> 13 <th></th> 14 <th>姓名</th> 15 <th>性别</th> 16 <th>年龄</th> 17 </tr> 18 </thead> 19 <tbody id="tableList"> 20 21 </tbody> 22 </table> 23 24 <!--插件引用--> 25 <script type="text/javascript" src="script/jquery.js"></script> 26 <script type="text/javascript" src="script/handlebars-v1.3.0.js"></script> 27 28 <!--Handlebars.js模版--> 29 <!--Handlebars.js模版放在script标签中,保留了html原有层次结构,模版中要写一些操作语句--> 30 <!--id可以用来唯一确定一个模版,type是模版固定的写法--> 31 <script id="table-template" type="text/x-handlebars-template"> 32 {{#each this}} 33 <tr> 34 <td>{{addOne @index}}</td> 35 <td>{{name}}</td> 36 <td>{{sex}}</td> 37 <td>{{age}}</td> 38 </tr> 39 {{#each family}} 40 <tr> 41 <td>{{../_index}}.{{@index}}</td> 42 <td>{{name}}</td> 43 <td>{{sex}}</td> 44 <td>{{age}}</td> 45 </tr> 46 {{/each}} 47 {{/each}} 48 </script> 49 50 <!--进行数据处理、html构造--> 51 <script type="text/javascript"> 52 var data = [{ 53 name: "张三", 54 sex: "男", 55 age: 35, 56 family: [{ 57 name: "张三儿子", 58 sex: "男", 59 age: 10, 60 },{ 61 name: "张三妻子", 62 sex: "女", 63 age: 33, 64 }] 65 },{ 66 name: "李四", 67 sex: "男", 68 age: 23, 69 family: [{ 70 name: "李四妻子", 71 sex: "女", 72 age: 23, 73 }] 74 },{ 75 name: "甜妞", 76 sex: "女", 77 age: 18, 78 family: [{ 79 name: "甜妞妈妈", 80 sex: "女", 81 age: 40, 82 },{ 83 name: "甜妞爸爸", 84 sex: "男", 85 age: 43, 86 },{ 87 name: "甜妞姥爷", 88 sex: "男", 89 age: 73, 90 }] 91 }]; 92 93 //注册索引+1的helper 94 var handleHelper = Handlebars.registerHelper("addOne",function(index){ 95 //利用+1的时机,在父级循环对象中添加一个_index属性,用来保存父级每次循环的索引 96 this._index = index+1; 97 //返回+1之后的结果 98 return this._index; 99 });100 //解析模版101 var handle = Handlebars.compile($("#table-template").html());102 //生成html103 var html = handle(data);104 //插入到页面105 $("#tableList").append(html);106 107 </script>108 </body>109 </html>
八、with-进入到某个属性(进入到某个上下文环境)
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <META http-equiv=Content-Type content="text/html; charset=utf-8"> 5 <title>with-进入到某个属性(进入到某个上下文环境) - by 杨元</title> 6 </head> 7 <body> 8 <h1>with-进入到某个属性(进入到某个上下文环境)</h1> 9 <!--基础html框架--> 10 <table> 11 <thead> 12 <tr> 13 <th>姓名</th> 14 <th>性别</th> 15 <th>年龄</th> 16 <th>兴趣爱好</th> 17 </tr> 18 </thead> 19 <tbody id="tableList"> 20 21 </tbody> 22 </table> 23 24 <!--插件引用--> 25 <script type="text/javascript" src=http://www.mamicode.com/"script/jquery.js"></script> 26 <script type="text/javascript" src=http://www.mamicode.com/"script/handlebars-1.0.0.beta.6.js"></script> 27 28 <!--Handlebars.js模版--> 29 <!--Handlebars.js模版放在script标签中,保留了html原有层次结构,模版中要写一些操作语句--> 30 <!--id可以用来唯一确定一个模版,type是模版固定的写法--> 31 <script id="table-template" type="text/x-handlebars-template"> 32 {{#each this}} 33 <tr> 34 <td>{{name}}</td> 35 <td>{{sex}}</td> 36 <td>{{age}}</td> 37 <td> 38 {{#with favorite}} 39 {{#each this}} 40 <p>{{name}}</p> 41 {{/each}} 42 {{/with}} 43 </td> 44 </tr> 45 {{/each}} 46 </script> 47 48 <!--进行数据处理、html构造--> 49 <script type="text/javascript"> 50 $(document).ready(function() { 51 //模拟的json对象 52 var data =http://www.mamicode.com/ [ 53 { 54 "name": "张三", 55 "sex": "0", 56 "age": 18, 57 "favorite": 58 [ 59 { 60 "name":"唱歌" 61 },{ 62 "name":"篮球" 63 } 64 ] 65 }, 66 { 67 "name": "李四", 68 "sex": "0", 69 "age": 22, 70 "favorite": 71 [ 72 { 73 "name":"上网" 74 },{ 75 "name":"足球" 76 } 77 ] 78 }, 79 { 80 "name": "妞妞", 81 "sex": "1", 82 "age": 18, 83 "favorite": 84 [ 85 { 86 "name":"电影" 87 },{ 88 "name":"旅游" 89 } 90 ] 91 } 92 ]; 93 94 //注册一个Handlebars模版,通过id找到某一个模版,获取模版的html框架 95 //$("#table-template").html()是jquery的语法,不懂的童鞋请恶补。。。 96 var myTemplate = Handlebars.compile($("#table-template").html()); 97 98 //将json对象用刚刚注册的Handlebars模版封装,得到最终的html,插入到基础table中。 99 $(‘#tableList‘).html(myTemplate(data));100 });101 </script>102 </body>103 </html>
在循环每名学生时,学生的favorite属性并不是一个普通的字符串,而又是一个json对象,确切的说是一个list,我们需要把学生的爱好全部取出来。
这时候就需要with命令,这个命令可以让当前的上下文进入到一个属性中,{{#with favorite}}表示进入到favorite属性的上下文中,而favorite属性中又是一个list,因此可以用{{#each this}}进行遍历,表示遍历当前上下文环境,对于每次遍历,都是map结构,取name属性,最终拿到所有兴趣爱好。
九、with-终极this应用
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <META http-equiv=Content-Type content="text/html; charset=utf-8"> 5 <title>with-终极this应用 - by 杨元</title> 6 </head> 7 <body> 8 <h1>with-终极this应用</h1> 9 <!--基础html框架-->10 <table>11 <thead>12 <tr>13 <th>姓名</th>14 <th>性别</th>15 <th>年龄</th>16 <th>兴趣爱好</th>17 </tr>18 </thead>19 <tbody id="tableList">20 21 </tbody>22 </table>23 24 <!--插件引用-->25 <script type="text/javascript" src=http://www.mamicode.com/"script/jquery.js"></script>26 <script type="text/javascript" src=http://www.mamicode.com/"script/handlebars-1.0.0.beta.6.js"></script>27 28 <!--Handlebars.js模版-->29 <!--Handlebars.js模版放在script标签中,保留了html原有层次结构,模版中要写一些操作语句-->30 <!--id可以用来唯一确定一个模版,type是模版固定的写法-->31 <script id="table-template" type="text/x-handlebars-template">32 {{#each this}}33 <tr>34 <td>{{name}}</td>35 <td>{{sex}}</td>36 <td>{{age}}</td>37 <td>38 {{#with favorite}}39 {{#each this}}40 <p>{{this}}</p>41 {{/each}}42 {{/with}}43 </td>44 </tr> 45 {{/each}}46 </script>47 48 <!--进行数据处理、html构造-->49 <script type="text/javascript">50 $(document).ready(function() {51 //模拟的json对象52 var data =http://www.mamicode.com/ [53 {54 "name": "张三",55 "sex": "0",56 "age": 18,57 "favorite":58 [59 "唱歌",60 "篮球"61 ]62 },63 {64 "name": "李四",65 "sex": "0",66 "age": 22,67 "favorite":68 [69 "上网",70 "足球"71 ]72 },73 {74 "name": "妞妞",75 "sex": "1",76 "age": 18,77 "favorite":78 [79 "电影",80 "旅游"81 ]82 }83 ];84 85 //注册一个Handlebars模版,通过id找到某一个模版,获取模版的html框架86 //$("#table-template").html()是jquery的语法,不懂的童鞋请恶补。。。87 var myTemplate = Handlebars.compile($("#table-template").html());88 89 //将json对象用刚刚注册的Handlebars模版封装,得到最终的html,插入到基础table中。90 $(‘#tableList‘).html(myTemplate(data));91 });92 </script>93 </body>94 </html>
本例和上例不同之处在于favorite属性中不再是map项,而是普通字符串,因此对于每个项,可以直接用{{this}}读取,this代表当前字符串。
所以说,this非常灵活,读者一定要大胆发挥想象力。
十、if-判断的基本用法
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <META http-equiv=Content-Type content="text/html; charset=utf-8"> 5 <title>if-判断的基本用法 - by 杨元</title> 6 </head> 7 <body> 8 <h1>if-判断的基本用法</h1> 9 <!--基础html框架-->10 <table>11 <thead>12 <tr>13 <th>姓名</th>14 <th>性别</th>15 <th>年龄</th>16 </tr>17 </thead>18 <tbody id="tableList">19 20 </tbody>21 </table>22 23 <!--插件引用-->24 <script type="text/javascript" src=http://www.mamicode.com/"script/jquery.js"></script>25 <script type="text/javascript" src=http://www.mamicode.com/"script/handlebars-1.0.0.beta.6.js"></script>26 27 <!--Handlebars.js模版-->28 <!--Handlebars.js模版放在script标签中,保留了html原有层次结构,模版中要写一些操作语句-->29 <!--id可以用来唯一确定一个模版,type是模版固定的写法-->30 <script id="table-template" type="text/x-handlebars-template">31 {{#each student}}32 {{#if name}}33 <tr>34 <td>{{name}}</td>35 <td>{{sex}}</td>36 <td>{{age}}</td>37 </tr>38 {{/if}}39 {{/each}}40 </script>41 42 <!--进行数据处理、html构造-->43 <script type="text/javascript">44 $(document).ready(function() {45 //模拟的json对象46 var data =http://www.mamicode.com/ {47 "student": [48 {49 "name": "张三",50 "sex": "0",51 "age": 1852 },53 {54 "sex": "0",55 "age": 2256 },57 {58 "name": "妞妞",59 "sex": "1",60 "age": 1861 }62 ]63 };64 65 //注册一个Handlebars模版,通过id找到某一个模版,获取模版的html框架66 //$("#table-template").html()是jquery的语法,不懂的童鞋请恶补。。。67 var myTemplate = Handlebars.compile($("#table-template").html());68 69 //将json对象用刚刚注册的Handlebars模版封装,得到最终的html,插入到基础table中。70 $(‘#tableList‘).html(myTemplate(data));71 });72 </script>73 </body>74 </html>
在遍历student时,由于数据缺失,并不是每一个学生都有name属性,我们不想显示没有name属性的学生,这时就需要if来做判断。
{{#if name}}可以用来判断当前上下文中有没有name属性,实际上,它是尝试去读取name属性,如果返回的为undefined、null、""、[]、false任意一个,都会导致最终结果为假。
if还支持多层次读取,例如:{{#if name.xxx}},这样写就假设name属性是一个map,检测name属性中是否包含xxx属性。
十一、由于if功力不足引出的Helper
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <META http-equiv=Content-Type content="text/html; charset=utf-8"> 5 <title>由于if功力不足引出的Helper - by 杨元</title> 6 </head> 7 <body> 8 <h1>由于if功力不足引出的Helper</h1> 9 <!--基础html框架-->10 <table>11 <thead>12 <tr>13 <th>姓名</th>14 <th>性别</th>15 <th>年龄</th>16 </tr>17 </thead>18 <tbody id="tableList">19 20 </tbody>21 </table>22 23 <!--插件引用-->24 <script type="text/javascript" src=http://www.mamicode.com/"script/jquery.js"></script>25 <script type="text/javascript" src=http://www.mamicode.com/"script/handlebars-1.0.0.beta.6.js"></script>26 27 <!--Handlebars.js模版-->28 <!--Handlebars.js模版放在script标签中,保留了html原有层次结构,模版中要写一些操作语句-->29 <!--id可以用来唯一确定一个模版,type是模版固定的写法-->30 <script id="table-template" type="text/x-handlebars-template">31 {{#each student}}32 {{#if name}}33 {{#compare age 20}}34 <tr>35 <td>{{name}}</td>36 <td>{{sex}}</td>37 <td>{{age}}</td>38 </tr>39 {{else}}40 <tr>41 <td>?</td>42 <td>?</td>43 <td>?</td>44 </tr>45 {{/compare}}46 {{/if}}47 {{/each}}48 </script>49 50 <!--进行数据处理、html构造-->51 <script type="text/javascript">52 $(document).ready(function() {53 //模拟的json对象54 var data =http://www.mamicode.com/ {55 "student": [56 {57 "name": "张三",58 "sex": "0",59 "age": 2360 },61 {62 "sex": "0",63 "age": 2264 },65 {66 "name": "妞妞",67 "sex": "1",68 "age": 1869 }70 ]71 };72 73 //注册一个Handlebars模版,通过id找到某一个模版,获取模版的html框架74 //$("#table-template").html()是jquery的语法,不懂的童鞋请恶补。。。75 var myTemplate = Handlebars.compile($("#table-template").html());76 77 //注册一个比较大小的Helper,判断v1是否大于v278 Handlebars.registerHelper("compare",function(v1,v2,options){79 if(v1>v2){80 //满足添加继续执行81 return options.fn(this);82 }else{83 //不满足条件执行{{else}}部分84 return options.inverse(this);85 }86 });87 88 //将json对象用刚刚注册的Handlebars模版封装,得到最终的html,插入到基础table中。89 $(‘#tableList‘).html(myTemplate(data));90 });91 </script>92 </body>93 </html>
很多时候,我们需要更加复杂的if判断逻辑,显然默认的if不能满足我们的需求。
本例中,利用Handlebars.js中Helper强大的扩展性,定义了一个compare,它用来比较两个数的大小,如果第一个数大于第二个数,满足条件继续执行,否则执行{{else}}部分。
Handlebars.registerHelper用来定义Helper,它有两个参数,第一个参数是Helper名称,第二个参数是一个回调函数,用来执行核心业务逻辑。本例中的函数,有三个参数,其中前两个参数是需要比较的两个数,第三个参数是固定的,就叫options,如果加了该参数,就说明这个Helper是一个Block,块级别的Helper,有一定的语法结构,调用的时候加#号,就像if那样。
关于options的使用,小菜所了解的就是这种用法,return options.fn(this);表示满足条件继续执行,也就是执行{{#compare }}和{{else}}之间的代码;return options.inverse(this);表示不满足条件,也就是执行{{else}}和{{/compare}}之间的代码。
由于这是一个块级别的Helper,所以调用要加#,例如:{{#compare age 20}},其中的age就是当前上下文中读取到的年龄,20是小菜随便写的值,意思是只要age比20大,就显示,否则全显示?。
读者可以看出,Helper中写的可以是任何js代码,现在想想,就知道Handlebars有多灵活了吧!!
十二、另一种Helper用法
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <META http-equiv=Content-Type content="text/html; charset=utf-8"> 5 <title>另一种Helper用法 - by 杨元</title> 6 </head> 7 <body> 8 <h1>另一种Helper用法</h1> 9 <!--基础html框架--> 10 <table> 11 <thead> 12 <tr> 13 <th>姓名</th> 14 <th>性别</th> 15 <th>年龄</th> 16 </tr> 17 </thead> 18 <tbody id="tableList"> 19 20 </tbody> 21 </table> 22 23 <!--插件引用--> 24 <script type="text/javascript" src=http://www.mamicode.com/"script/jquery.js"></script> 25 <script type="text/javascript" src=http://www.mamicode.com/"script/handlebars-1.0.0.beta.6.js"></script> 26 27 <!--Handlebars.js模版--> 28 <!--Handlebars.js模版放在script标签中,保留了html原有层次结构,模版中要写一些操作语句--> 29 <!--id可以用来唯一确定一个模版,type是模版固定的写法--> 30 <script id="table-template" type="text/x-handlebars-template"> 31 {{#each student}} 32 {{#if name}} 33 {{#compare age 20}} 34 <tr> 35 <td>{{name}}</td> 36 <td>{{transformat sex}}</td> 37 <td>{{age}}</td> 38 </tr> 39 {{else}} 40 <tr> 41 <td>?</td> 42 <td>?</td> 43 <td>?</td> 44 </tr> 45 {{/compare}} 46 {{/if}} 47 {{/each}} 48 </script> 49 50 <!--进行数据处理、html构造--> 51 <script type="text/javascript"> 52 $(document).ready(function() { 53 //模拟的json对象 54 var data =http://www.mamicode.com/ { 55 "student": [ 56 { 57 "name": "张三", 58 "sex": "0", 59 "age": 23 60 }, 61 { 62 "name": "李四", 63 "sex": "0", 64 "age": 18 65 }, 66 { 67 "name": "妞妞", 68 "sex": "1", 69 "age": 21 70 } 71 ] 72 }; 73 74 //注册一个Handlebars模版,通过id找到某一个模版,获取模版的html框架 75 //$("#table-template").html()是jquery的语法,不懂的童鞋请恶补。。。 76 var myTemplate = Handlebars.compile($("#table-template").html()); 77 78 //注册一个比较大小的Helper,判断v1是否大于v2 79 Handlebars.registerHelper("compare",function(v1,v2,options){ 80 if(v1>v2){ 81 //满足添加继续执行 82 return options.fn(this); 83 }else{ 84 //不满足条件执行{{else}}部分 85 return options.inverse(this); 86 } 87 }); 88 89 //注册一个翻译用的Helper,0翻译成男,1翻译成女 90 Handlebars.registerHelper("transformat",function(value){ 91 if(value=http://www.mamicode.com/=0){ 92 return "男"; 93 }else if(value=http://www.mamicode.com/=1){ 94 return "女"; 95 } 96 }); 97 98 //将json对象用刚刚注册的Handlebars模版封装,得到最终的html,插入到基础table中。 99 $(‘#tableList‘).html(myTemplate(data));100 });101 </script>102 </body>103 </html>
小菜刚刚提到过,带options参数的Helper是块级别的,而不带的,相当于行内级别的Helper。
从例子一开始,性别就是用0、1代码表示的,但实际情况下我们需要转换成汉字,transformat这个Helper需要一个参数,根据不同的代码,返回男女,这样调用{{transformat sex}},其中sex是从当前上下文中读取的性别代码。
十三、关于HTML编码
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <META http-equiv=Content-Type content="text/html; charset=utf-8"> 5 <title>关于HTML编码 - by 杨元</title> 6 </head> 7 <body> 8 <h1>关于HTML编码</h1> 9 <!--基础html框架-->10 <table>11 <thead>12 <tr>13 <th>姓名</th>14 <th>性别</th>15 <th>年龄</th>16 <th>个人主页</th>17 </tr>18 </thead>19 <tbody id="tableList">20 21 </tbody>22 </table>23 24 <!--插件引用-->25 <script type="text/javascript" src=http://www.mamicode.com/"script/jquery.js"></script>26 <script type="text/javascript" src=http://www.mamicode.com/"script/handlebars-1.0.0.beta.6.js"></script>27 28 <!--Handlebars.js模版-->29 <!--Handlebars.js模版放在script标签中,保留了html原有层次结构,模版中要写一些操作语句-->30 <!--id可以用来唯一确定一个模版,type是模版固定的写法-->31 <script id="table-template" type="text/x-handlebars-template">32 {{#each student}}33 <tr>34 <td>{{name}}</td>35 <td>{{sex}}</td>36 <td>{{age}}</td>37 {{#compare age 20}}38 <td>{{homePage}}</td>39 {{else}}40 <td>{{{homePage}}}</td>41 {{/compare}}42 </tr> 43 {{/each}}44 </script>45 46 <!--进行数据处理、html构造-->47 <script type="text/javascript">48 $(document).ready(function() {49 //模拟的json对象50 var data =http://www.mamicode.com/ {51 "student": [52 {53 "name": "张三",54 "sex": "0",55 "age": 18,56 "homePage":"<a href=‘javascript:void(0);‘>张三的个人主页</a>"57 },58 {59 "name": "李四",60 "sex": "0",61 "age": 22,62 "homePage":"<a href=‘javascript:void(0);‘>李四的个人主页</a>"63 },64 {65 "name": "妞妞",66 "sex": "1",67 "age": 19,68 "homePage":"<a href=‘javascript:void(0);‘>妞妞的个人主页</a>"69 }70 ]71 };72 73 //注册一个Handlebars模版,通过id找到某一个模版,获取模版的html框架74 //$("#table-template").html()是jquery的语法,不懂的童鞋请恶补。。。75 var myTemplate = Handlebars.compile($("#table-template").html());76 77 //注册一个比较数字大小的Helper,有options参数,块级Helper78 Handlebars.registerHelper("compare",function(v1,v2,options){79 //判断v1是否比v2大80 if(v1>v2){81 //继续执行82 return options.fn(this);83 }else{84 //执行else部分85 return options.inverse(this);86 }87 });88 89 //将json对象用刚刚注册的Handlebars模版封装,得到最终的html,插入到基础table中。90 $(‘#tableList‘).html(myTemplate(data));91 });92 </script>93 </body>94 </html>
通过{{}}取出来的内容,都会经过编码,也就是说,如果取出的内容中包含html标签,会被转码成纯文本,不会被当成html解析,实际上就是做了类似这样的操作:把<用<替代。
这样做是很好的,既可以显示html代码,又可以避免xss注入。这个功能在做代码展示的时候是非常有用的。
但是有时候我们可能需要解析html,不要转码,很简单,把{{}}换成{{{}}}就可以啦。
本例中,年龄大于20的童鞋个人主页被编码,直接展示出来;而年龄小于20的童鞋,个人主页被当成html解析,显示的是一个超链接。
十四、结束语
有了这些功能,[ajax+json+Handlebars]替代[vo+el表达式]不成问题,新时代的曙光已经来临,最佳解决方案在此,您还等什么?
教程到此结束。。。祝读者学习愉快。。。
转:http://www.cnblogs.com/iyangyuan/p/3471379.html
js模版引擎handlebars.js实用教程