`
jljlpch
  • 浏览: 323197 次
  • 性别: Icon_minigender_1
  • 来自: 南昌
社区版块
存档分类
最新评论

Ext util.XTemplate分析

    博客分类:
  • Ext
阅读更多

 Ext.Template完成了基本的插值功能,现在Ext.uitl.Xtemplate要完成指令功能。只有这样模板才是完整的。其实Ext.Template插值功能,也是差强人意的,比如没有实现点串(user.name)的插值,其实这个实现起来很简单。在ExtExt.uitl.Xtemplate中,实现点串功能,在{[]}可以使用内嵌的Js代码。实现在for,if,exec指令,

对于for指令,感觉有点不爽是采用命名映射的方式,很多时候我们只要数组中的一部分,比如:a[1..5]。我还要在for的指令内嵌if指令来判断,导致很麻烦。在freemarker就能直接用。而在Xtemplate中,for=”{[]}”这样的都解析不了。有点讲不过去。

 
对于if 指令,判断的条件肯定是js语句的表达式,比如: <tpl if="this.isGirl(name)">',但是不支持else,eles if 等指令。
但是总的来说,Ext.uitl.Xtemplate完成功能蛮强大的。

2.1Xtemplate的构建


上面我们把for,if 这样的操作称为指令。在Xtemplate换了一种称法。称为子模板。Ext.template实现了基本的插值功能。我们可以想像一下,在<tpl for=””></tpl>类似这样的操作中,标签中间的内容肯定都是插值操作,称为叶子模板。如果<tpl for=””></tpl>嵌套<tpl for=””></tpl>呢?那标签中间的内容就不是插值操作了,称为节点模板。

那么能不能把节点模板转化为叶子模板呢,这样一下,就可以重用Ext.Template的插值功能,我们可以把节点模板的子模板做字符变换,变成Template的插值的形式,例如:{xtpl1}。这样一来所的有子模板都可以转换成叶子模板,可以采用Ext.Template的插值操作。

不过上面还有一个问题没有考虑到,那就是每个子模板的标签中的内容,如何保存,如<tpl for=””></tpl>,for属性的内容。如何去知道是for指令,还是if指令的模板呢?这个问题我们可以在转换子模板为叶子模板时,把其相关的内容或信息存在数组中,然后分配一个Id,上面的{xtpl1}中的1就可以是id.这样的就可以找到每个模板的全部信息。
举个例子:

 

'<tpl><p>Name: {name}</p><p>Kids: 
<tpl for="kids"> 
<tpl if="age > 1"><p>{name}</p> <p>Dad: {parent.name}</p> 
</tpl> 
</tpl></p> 
</tpl>

 '


这是嵌套的模板,我们如何把它转换成上面所说的所有子模板都换成叶子模板:第一步,我们可以把最内部的叶子模板采用{xtpl1}代替。

 

'<tpl><p>Name: {name}</p><p>Kids: 
<tpl for="kids"> 
{xtpl1} 
</tpl></p> 
</tpl>' 

 


同时往已建立好一个数组中加{xtpl1}的信息,首先得存标签的内容吧:还有if的这个指令类别,还有age>1这个expr.
Xtp1的对象就应该如这样:{id:1,body: <p>{name}</p> <p>Dad: {parent.name},type:if params: ‘age > 1’};当然我们可以采用更好的方式。

接下第二步就是

 

'<tpl><p>Name: {name}</p><p>Kids: 
{xtpl2}</p> 
</tpl>' 
Xtpl2={id:2,body: {xtpl1},type:for, params: ‘kids’} 

 

再接下来:

'{xtpl3}’ 
Xtpl3={ id:3,body: p>Name: {name}</p><p>Kids: {xtpl2}</p>,type:’’, params: ’’}; 

 
这样就把嵌套的模板分成了可以采用Ext.Template操作的几个子模板。我们只要定位了最后一个模板,就可以通过解析改模板的插值来解决其嵌套的各种子模板。之后就采用递推的方式去解析所有子模板。我们这最后一个模板称为根模板。

那么又如何去实现按上面的遍历的顺序去遍历整个模板内容呢。通过regexp能很方便的实现,只在regexp中定义其中的内容不包括子模板就可以了。只要字符没有<tpl ..就可以了。找到了,把其相关的信息存在数组中,把其生成Id以插值的形式replace改子模板就可以了。之后一直循环,直到没有模板为止。最后一个模板肯定是根模板。

看一下Ext.util.Xtemplate是怎么实现的?

 

//找到叶子模板,把模板中的内容存在$1中。 
var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/; 
//找到<tpl for=’name’)中name的值,存在$1中 
var nameRe = /^<tpl\b[^>]*?for="(.*?)"/; 
//找到<tpl if =’expr)中expr的值,存在$1中 
var ifRe = /^<tpl\b[^>]*?if="(.*?)"/; 
//找到<tpl if =’exec)中exec的值,存在$1中 
var execRe = /^<tpl\b[^>]*?exec="(.*?)"/; 

var m, id = 0; 
var tpls = []; 

//找到内面没有嵌套的<tpl></tpl>的子模板 
while(m = s.match(re)){ 
var m2 = m[0].match(nameRe); 
var m3 = m[0].match(ifRe); 
var m4 = m[0].match(execRe); 
var exp = null, fn = null, exec = null; 
//m2,m3,m4[1]保存是子匹配的子符串内容。 
var name = m2 && m2[1] ? m2[1] : ''; 
//支持if 指令的子模板 支持if 的判断的表达式操作。 
if(m3){ exp = m3 && m3[1] ? m3[1] : null; 
if(exp){ 
fn = new Function('values', 'parent', 'xindex', 'xcount', 'with(values){ return '+(Ext.util.Format.htmlDecode(exp))+'; }'); 
} 
} 
if(m4){ exp = m4 && m4[1] ? m4[1] : null; 
if(exp){ 
exec = new Function('values', 'parent', 'xindex', 'xcount', with(values){ '+(Ext.util.Format.htmlDecode(exp))+'; }'); 
} 
} 
//支持for 指令的子模板,不过命名只支持.,..,name,不支持表达式。 
if(name){ 
switch(name){ 
case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break; 
case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break; 
default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }'); 
} 
} 
//为每次找到子模板构建相关的信息,存在数组中,还是觉得这种把for,if,exec 
//这三种指令这样写不好,不如用如上面的type呢。 
tpls.push({ 
id: id, 
target: name, 
exec: exec, 
test: fn, 
//m[1]指<tpl></tpl>中间的所有内容。 
body: m[1]||'' 
}); 
//把子模板的内容replace成插值的形式。 
s = s.replace(m[0], '{xtpl'+ id + '}'); 
++id; 
} 

 

2.2compileTpl (tpl)


上面的构建方法中转换就是为了能利用Ext.template的形式或思想来处理插值。Ext.Template为模板编译了complied的方法,Xtemplate的子模板也应该要有每个自己相对应的compiled()方法。因为模板的内容不同,所以方法就不一样了。之后要apply某一个子模板,只在调用其compiled()方法就可以了。

Xtemplate的对插值的解析和template对插值的解析差不多。多了几个功能,不过是多了几个判断的语句。现在一个排在面前的问题是如何解析如{xtpl1}这样的插值。解决这个就解决了模板的嵌套问题。{xtpl}的位置用什么来替换好呢?我们要的是在执行改模板时,就自动执行该模板的子模板,并把相对应的值传到子模板中去。如果子模板还有子模板的话,执行子模板时,又会自动执行子模板的子模板。也就是只要调用根模板的complied(xx,yy)就可以自动执行所有的模板。

这也就是要求在compile模板的过程,得到执行子模板的函数写入取代{xtpl1}的插值的地方。只有这样,在调用只要调用根模板的complied(xx,yy)就可以自动执行所有的子模板了。

 
其形式如下:

 

tpl3: tpl.compiled = function(values, parent, xindex, xcount){ 
return ['<p>Name: ',values['name']===undefined?''values['name'],'</p> <p>Kids: ', this.applySubTemplate(2, values, parent, xindex, xcount),'' ].join('');}; 


tpl2: tpl.compiled = function(values, parent, xindex, xcount){ 
return ['',this.applySubTemplate(1, values, parent, xindex, xcount),''].join('');}; 


tpl1: tpl.compiled = function(values, parent, xindex, xcount){ 
return ['<p>',values['name']===undefined?''values['name'],'</p> <p>Dad:' ,parent.name===undefined?''parent.name,'</p>'].join('');}; 

 

tpl3是根模板,其执行applySubTemplate(2, values, parent, xindex, xcount),'' ],这是tpl2模板,tpl2模板执行this.applySubTemplate(1, values, parent, xindex, xcount)的tpl1模板。

接下仔细分析xtemplate怎么处理插值的,与Template有什么不同。
Xtemplate插值的格式为

 

{name[:][format][(params)][math expr]}

 

可以看得出来,插值操作多了一个math expr的功能,如{age+5}。对于name,

regexp为([\w-\.\#]+),比template多了.,#。

 

.表达当前的Values,#表示当前的行号。如是parent.name责可以表明支持点串。


\{([\w-\.\#]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?(\s?[\+\-\*\\]\s?[\d\.\+\-\*\\\(\)]+)?\}/g

 

是分析{}的regexp.
Xtemplate还支持inline的Js 语句,其格式为:


{[js expr]}如:class="{[xindex % 2 === 0 ? "even" : "odd"]}。

compileTpl : function(tpl){ 
var fm = Ext.util.Format; 
var useF = this.disableFormats !== true; 
var sep = Ext.isGecko ? "+" : ","; 
var fn = function(m, name, format, args, math){ 
//该模板中的子模板 
if(name.substr(0, 4) == 'xtpl'){ 
//',this.applySubTemplate(id, values, parent, xindex, xcount),' 
return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent, xindex, xcount)'+sep+"'"; 
} 
var v; 
if(name === '.'){ 
//values仅仅是当前模板中 
v = 'values'; 
}else if(name === '#'){ 
v = 'xindex'; 
}else if(name.indexOf('.') != -1){ 
//支持点串,如parent.name 
v = name; 
}else{ 
v = "values['" + name + "']"; 
} 

if(math){ 
//支持变量的处理, 如:{age+5} 
v = '(' + v + math + ')'; 
} 
if(format && useF){ 
args = args ? ',' + args : ""; 
if(format.substr(0, 5) != "this."){ 
//fm.xxf( 
format = "fm." + format + '('; 
}else{ 
//this.call("isgirl", 
format = 'this.call("'+ format.substr(5) + '", '; 
args = ", values"; 
} 
}else{ 
args= ''; format = "("+v+" === undefined ? '' : "; 
} 

//',fm.xxformat(values['namex']||values||xindex||math Expression,xx,yy),' 
//',this.call('isGirl',values['namex']||values||xindex||math Expression,values),' 
//',(values['namex']||values||xindex||math Expression===undefined?'':values['namex']||values||xindex||math Expression]),' 

return "'"+ sep + format + v + args + ")"+sep+"'"; 
}; 
var codeFn = function(m, code){ 
//',(xx.bb),' 
return "'"+ sep +'('+code+')'+sep+"'"; 
}; 

var body; 
// branched to use + in gecko and [].join() in others 
if(Ext.isGecko){ 
body = "tpl.compiled = function(values, parent, xindex, xcount){ return '" + 
tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn).replace(this.codeRe, codeFn) + 
"';};"; 
}else{ 
//该函数是每个模板中的。 

/* 
* '<tpl><p>Name: {name}</p><p>Kids: 
* <tpl for="kids"> 
* <tpl if="age > 1"><p>{name}</p> <p>Dad: {parent.name}</p> 
* </tpl> 
* </tpl></p> 
* </tpl>' 

tpl1:body:'<p>{name}</p> <p>Dad: {parent.name}</p>', test=age > 1 ids=1 

* '<tpl><p>Name: {name}</p><p>Kids: 
* <tpl for="kids"> 
* {xtpl1} 
* </tpl></p> 
* </tpl>' 

tpl2:body:{xtpl1},name= kids id=2 

* '<tpl><p>Name: {name}</p><p>Kids: {xtpl2}</p> 
* </tpl>' 

tpl3 body= '<p>Name: {name}</p> <p>Kids: {xtpl2}</p>' id=3 

* '{xtpl3}' 

tpl3: tpl.compiled = function(values, parent, xindex, xcount){ 
return ['<p>Name: ',values['name']===undefined?''values['name'],'</p> <p>Kids: ', 
this.applySubTemplate(2, values, parent, xindex, xcount),'' ].join('');}; 

tpl2: tpl.compiled = function(values, parent, xindex, xcount){ 
return ['',this.applySubTemplate(1, values, parent, xindex, xcount),''].join('');}; 

tpl1: tpl.compiled = function(values, parent, xindex, xcount){ 
return ['<p>',values['name']===undefined?''values['name'],'</p> <p>Dad:' 
,parent.name===undefined?''parent.name,'</p>'].join('');}; 

* */ 
body = ["tpl.compiled = function(values, parent, xindex, xcount){ return ['"]; 
body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn).replace(this.codeRe, codeFn)); 
body.push("'].join('');};"); 
body = body.join(''); 
} 
eval(body); 

/* tpl.compiled = function(values, parent, xindex, xcount){ 
return ['',this.applySubTemplate(3, values, parent, xindex, xcount),''].join('');}; 

*/ 
return this; 
}, 

 
2.3applySubTemplate


对于applySubTemplate,我们只要理解一个问题,就是parent,child的关系。对于if,exec指令是不改变value,parent的。因为子模板是通过递规的形式把values, parent, xindex, xcount传入到子模板的子孙。在这种递规是通过调用applySubTemplate实现,values,parent在传递过程就可以改变了。

只有for指令的模板会改变values, parent的值的。对于for 指令的模板,其所有的子模板的parent等于for模板的的values.for 数组之上的values.the模板内容及子模板的values都是等于for的list数组中的当前项。

看起来很方便,其实反而不好理解和使用。还不如使用 freemaker模板的<#list users as u> 通过u的引用的得到数组的当前项。

//处理values,parent,等参数的传递。for子模板把分成父子模板关系了 
applySubTemplate : function(id, values, parent, xindex, xcount){ 
var t = this.tpls[id]; 
//if 中return false,就不处理该if子模板了。 
if(t.test && !t.test.call(this, values, parent, xindex, xcount)){ 
return ''; 
} 
if(t.exec && t.exec.call(this, values, parent, xindex, xcount)){ 
return ''; 
} 
// t.target就是 for的name. 
var vs = (t.target ? t.target.call(this, values, parent) : values); 

//对于for的子模板,parent就指向 for 的 values[]. 
parent =( t.target ? values : parent); 
if(t.target && Ext.isArray(vs)){ 
//对于for loop,loop all element,and join to str . 
var buf = []; 
for(var i = 0, len = vs.length; i < len; i++){ 
buf[buf.length] = t.compiled.call(this, vs[i], parent, i+1, len); 
} 
return buf.join(''); 
} 
return t.compiled.call(this, vs, parent, xindex, xcount); 
}, 

 

                                                                                     prk       2008-07-28

分享到:
评论
3 楼 czpae86 2008-12-21  
2 楼 jljlpch 2008-10-28  
sp42 写道

引用
而在Xtemplate中,for=”{[]}”这样的都解析不了。有点讲不过去。数组应该可以自动辨认的吧?

它支持{[]}插值的inline的Javascript代码。但是不能采用对于for指令而言则不能支持{[]}
1 楼 sp42 2008-10-27  
引用
而在Xtemplate中,for=”{[]}”这样的都解析不了。有点讲不过去。

数组应该可以自动辨认的吧?

相关推荐

    一个简单的Ext.XTemplate的实例代码

    在本实例中,我们看到的是如何使用Ext.XTemplate来展示一个省份与城市的树形结构。下面将详细解释这个实例中的关键知识点。 1. **Ext.lib.Ajax.request**: 这个方法是用来发起异步的Ajax请求,向服务器'/Index/...

    EXT核心API详解

    23、Ext.XTemplate类 ………………… 21 24、Ext.data.Connection类 ……………… 22 25、Ext.Ajax类 ………………………… 22 26、Ext.data.Record类 ………………… 23 27、Ext.data.DataProxy类 …………… 24 28...

    ExtJS入门教程(超级详细)

    23、Ext.XTemplate类 ………………… 21 24、Ext.data.Connection类 ……………… 22 25、Ext.Ajax类 ………………………… 22 26、Ext.data.Record类 ………………… 23 27、Ext.data.DataProxy类 …………… 24 28...

    Ext Js权威指南(.zip.001

    8.1.4 超级模板:ext.xtemplate(包括ext.xtemplateparser和ext.xtemplatecompiler) / 393 8.1.5 模板的方法 / 396 8.2 组件的基础知识 / 396 8.2.1 概述 / 396 8.2.2 组件类的整体架构 / 397 8.2.3 布局类的...

    EXT核心API详解.doc

    21. **Ext.XTemplate类**:用于创建复杂的模板,生成动态HTML。 22. **Ext.data.Connection类**和**Ajax类**:提供了与服务器的数据交换功能,支持异步请求。 23. **Ext.data.Record类**:数据记录模型,用于存储...

    ext_image.rar_ext_image_extjs_extjs 4.2 img_extjs image_extjs图片上

    模板(`Ext.XTemplate`或`Ext.util.Format`)则用于格式化和渲染每个图片项的HTML。 图片上传功能通常借助于ExtJS的表单组件(`Ext.form.Panel`)和上传字段(`Ext.form.field.File`)。在ExtJS 4.2中,上传组件...

    Ext深入浅出 数据传输

    11.2.5 复杂模板XTemplate..........279 11.3 用Ext.Utils.CSS切换主题..............281 11.4 悬停提示.................................................282 11.4.1 初始化........................................

    EXTJS___API详解

    19. **Ext.util.DelayedTask 和 Ext.util.TaskRunner 类**:用于定时任务的执行,DelayedTask用于延迟调用,TaskRunner用于周期性任务。 20. **Ext.util.TextMetrics 类**:测量文本在DOM中的尺寸,常用于动态调整...

    Ext API详解--笔记

    `EXT核心API详解(七)-Ext.KeyNav KeyMap JSON Format DelayedTask TaskRunner TextMetrics XTemplate.txt`涉及键盘导航、键映射、JSON处理、格式化、延迟任务、任务调度器、文本测量以及XTemplate等内容。...

    精通JS脚本之ExtJS框架.part1.rar

    4.4.4 Ext.util.Observable事件 4.4.5 Ext.EventManager事件 4.4.6 Ext.EventObject事件 4.5 各种事件登记方式 4.5.1 传统式登记 4.5.2 内联式登记 4.5.3 Dom Level2登记 4.6 高级组件事件 4.7 ExtJS键盘...

    精通JS脚本之ExtJS框架.part2.rar

    4.4.4 Ext.util.Observable事件 4.4.5 Ext.EventManager事件 4.4.6 Ext.EventObject事件 4.5 各种事件登记方式 4.5.1 传统式登记 4.5.2 内联式登记 4.5.3 Dom Level2登记 4.6 高级组件事件 4.7 ExtJS键盘...

    ExtJs4_笔记.docx

    4. **函数执行时间控制**:提供`Ext.util.DelayedTask`等工具,延迟或定时执行函数。 5. **键盘事件侦听**:支持键盘事件监听,如`keydown`、`keyup`,便于实现键盘驱动的交互。 **第三章 Ext.Ajax 对ajax的支持** ...

    extjs.docx

    6. **Ext.XTemplate**:XTemplate是一种强大的模板引擎,用于动态生成HTML内容。它可以结合数据对象生成复杂结构的HTML,常用于列表、表格等组件的数据展示。 7. **Ext.define**:定义新的类或扩展已存在的类。这是...

    Ext3.0英文API.CHM

    - **Ext.util**:包含各种实用工具函数,如Array、Function、Object等。 - **Ext.data**:数据管理模块,包括数据存储、模型定义、数据交换等。 - **Ext.grid**:提供网格视图,支持数据分页、排序、过滤等功能。...

    extapi

    最后,"EXT核心API详解(七)-Ext.KeyNav KeyMap JSON Format DelayedTask TaskRunner TextMetrics XTemplate.txt"涉及键盘导航(KeyNav)、键映射(KeyMap)、JSON格式化、延迟任务(DelayedTask)、任务调度器...

Global site tag (gtag.js) - Google Analytics