一、为什么选择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="script/jquery.js"></script> 2 <script type="text/javascript" src="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="script/jquery.js"></script> 25 <script type="text/javascript" src="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 = { 45 "student": [ 46 { 47 "name": "张三", 48 "sex": "0", 49 "age": 18 50 }, 51 { 52 "name": "李四", 53 "sex": "0", 54 "age": 22 55 }, 56 { 57 "name": "妞妞", 58 "sex": "1", 59 "age": 18 60 } 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="script/jquery.js"></script> 25 <script type="text/javascript" src="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 = [ 45 { 46 "name": "张三", 47 "sex": "0", 48 "age": 18 49 }, 50 { 51 "name": "李四", 52 "sex": "0", 53 "age": 22 54 }, 55 { 56 "name": "妞妞", 57 "sex": "1", 58 "age": 18 59 } 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="script/jquery.js"></script> 14 <script type="text/javascript" src="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 = [{ 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="script/jquery.js"></script> 26 <!--注意!这个例子用的是新版本handlebars,旧版本不支持--> 27 <script type="text/javascript" src="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 = { 48 "student": [ 49 { 50 "name": "张三", 51 "sex": "0", 52 "age": 18 53 }, 54 { 55 "name": "李四", 56 "sex": "0", 57 "age": 22 58 }, 59 { 60 "name": "妞妞", 61 "sex": "1", 62 "age": 19 63 } 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: 35 48 },{ 49 name: "李四", 50 sex: "男", 51 age: 23 52 },{ 53 name: "甜妞", 54 sex: "女", 55 age: 18 56 }]; 57 58 //解析模版 59 var handle = Handlebars.compile($("#table-template").html()); 60 //生成html 61 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: 35 48 },{ 49 name: "李四", 50 sex: "男", 51 age: 23 52 },{ 53 name: "甜妞", 54 sex: "女", 55 age: 18 56 }]; 57 58 //注册索引+1的helper 59 var handleHelper = Handlebars.registerHelper("addOne",function(index){ 60 //返回+1之后的结果 61 return index+1; 62 }); 63 //解析模版 64 var handle = Handlebars.compile($("#table-template").html()); 65 //生成html 66 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 //生成html 103 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="script/jquery.js"></script> 26 <script type="text/javascript" src="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 = [ 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="script/jquery.js"></script> 26 <script type="text/javascript" src="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 = [ 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="script/jquery.js"></script> 25 <script type="text/javascript" src="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 = { 47 "student": [ 48 { 49 "name": "张三", 50 "sex": "0", 51 "age": 18 52 }, 53 { 54 "sex": "0", 55 "age": 22 56 }, 57 { 58 "name": "妞妞", 59 "sex": "1", 60 "age": 18 61 } 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="script/jquery.js"></script> 25 <script type="text/javascript" src="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 = { 55 "student": [ 56 { 57 "name": "张三", 58 "sex": "0", 59 "age": 23 60 }, 61 { 62 "sex": "0", 63 "age": 22 64 }, 65 { 66 "name": "妞妞", 67 "sex": "1", 68 "age": 18 69 } 70 ] 71 }; 72 73 //注册一个Handlebars模版,通过id找到某一个模版,获取模版的html框架 74 //$("#table-template").html()是jquery的语法,不懂的童鞋请恶补。。。 75 var myTemplate = Handlebars.compile($("#table-template").html()); 76 77 //注册一个比较大小的Helper,判断v1是否大于v2 78 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="script/jquery.js"></script> 25 <script type="text/javascript" src="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 = { 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==0){ 92 return "男"; 93 }else if(value==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="script/jquery.js"></script> 26 <script type="text/javascript" src="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 = { 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参数,块级Helper 78 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