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

Ext.template分析

    博客分类:
  • Ext
阅读更多

          说起模板,很多人都会想起FreeMaker。什么是模板呢?模板就是按预前给定的模样生产出来。这个预前给定的模样就是模板。在程序开发上的模板有一点不同,它不是完全一模一样的。

 
    举个例子:比如我要在页面显示某人的一些信息

 

<div style=”….”>小王</div><div style=”….”>1983-09-24</div>

 

    这是一段内容。很多时间我们需要变化的仅仅是name,birthday这两个部分。如果对于小王,小李等不同的人,每次都要完全重写这一段内容。首先很烦琐,修改起来也麻烦,style是统一的,如果只要修改一个就能统一修改多好啊。
于是就出现了模板,把静态不变的内容做成模板,把动态变化的内容采用插值(${})的形式插入。构成我们要的内容。说到这里,JSP就是一个模板。


      为了统一名词,在这里把动态变化的内容称为插值。把静态不变的内容称为模板静态部分。把插值和静态部分构成的整体称为模板内容。把当模板内容中的插值采用了实际值时,我们就称为模板生成内容。


     现在开始谈论Ext.Template吧。对于Javascript模板,其最终的结果就是生成在浏览器中能显示的内容。对于显示的内容来说,首先会想法是格式。模板静态部分可以写死格式,这个插值部分怎么办呢?比如比如出生年月不想采用1983-09-24而是采用1983年9月24日。对于这样的格式转换,每种Lib都会有通用的函数。现在的问题是如果把这一些函数引入来?其实还有一些不通用的格式转换,这部分涉及到业务逻辑。比如:小王,我们想把name的插值部分改成王先生,或王小姐,这就是和性别相关了。这种格式转换的代码只能用户自己写。那么又如何加入呢?

1.1构建Template对象

Ext.Template就是解决上面一些问题的。它的插值符号采用{},其格式采用

 

{name[:][format][(params)]}。

 

[]表达是可选的。可以分成两部分:第一部分是传入动态值的name,第二部分是调用格式化函数。

 

这个函数可以是Ext.util.Format中的函数,也可以是自己写的函数,对于自己写的函数,要注册到本Template的实例中来。函数的第一个参数是默认的后台传入的,是name的值。对于Ext.util.Format中的函数来讲,如ellipsis(10),只要函数名就可以,而不能用Ext.util.Format. ellipsis。第一参数是默认的后台传入的name的值,之后的参数传入就是该调用的参数的顺序,如ellipsis第二个参数就是10。如果只有一个参数,函数的()可省,如{name:trim}。对于自己实现的模式化函数,后台只传两个参数,一是该name的值,二是改模板传入的所有的动态值(values)。这也就是说我们在实现自己的格式化函数时,只能按这样的格式去写。接下来就怎么构建Template。

构建一个Template的操作很简单。首先我们得定义模板内容。定义模板内容其实就是定义Html Segment。把动态的部分采用插值形式书写就可以。如:'

 

<span class="{cls}">{name:trim} {value:ellipsis(10)}</span>。

 

插值只要采用上面介绍的格式。


Ext.Template的构建函数采用更灵活的方式让我们去传入定义好的模板内容。我们可以把模板内容以数组的形式传进去,还可以以多个参数的形式传入(如

 

var t = new Ext.Template( '<div name="{id}">','<span class="{cls}">{name:trim} </span>', '</div>');)。

 

Ext.Template的构建函数会自动按顺序串接这些字符串为模板内容。


传入了模板内容,可能我们还要进行一些配置 比如:不要编译,不要格式化,自己实现的格式化函数。Ext.Template的构建函数还提供一个config,我们可能通过这个参数来高设定我们需要的配置。如

 

{ compiled:true,disableFormats:true ,ToPrivateName:function(value,Values){}};。 

 
1.2applyTemplate(Values)

构建Template对象之后,接下来要用它来生成模板生成内容。Ext.Template有很多的实用方法,但最终都是调用applyTemplate(Values)来生成内容。而applyTemplate(Values)函数的作用也就是把模板中插值用传入Values中的值来取代。模板内容是字符串形式,插值也是字符串形式,而value也可以看做字符串形式。这种对于string的操作,当然就是要用string.replace()函数。

回忆一下replace(regexp,str)。我们会发现我们的需求和与replace(regexp,str)有点不一样。我要的每当我找到一个插值(如{ name })时,我替换的内容是要根据插值(如{ name })中的内容(name)从传进来的values找到相同的名字的变量值。实际的会更复杂一点,假如插值中有格式化函数({value:ellipsis(10)}),那么我要取得改函数名,还有函数的参数值。也就是我们的需求是replace(regexp,str)的第二个参数是function,而且能动态从第一个参数regexp的$1--$99传入该函数的参数值。

好像string. replace(regexp,str)没有办法实现,其实不然,它还有很少见的用法,就是为满足上面的需求。在baidu.google中也基本搜不到。我们可以到mozilla 的文档去看看http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Global_Objects:String:repl,在mozilla 的文档中有这样定义:

 

var newString = str.replace(regexp/substr, newSubStr/function[, flags]); 

 

 一种很少的的用法就是第二个参数可以是function ,而第一个参数中的regexp 中$1,$2做为函数的参数传到函数中,并执行该函数,返回结果,这结果当然是字符串,函数的第一个参数是该regexp 所匹配的字符串的内容,第二,三。。。的按$1,$2的顺序传参数。

接下来,我们就应该了解该regexp的用法,把插值{name[:][format][(params)]}分成$1,$2,$3这样的值做为参数值传到replace(reg,function)的函数参数中去。/\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g就完成这个任务,把插值中的name存在$1中,把format存在$2中,把params存在$3中。String. replace(reg,function)会把reg查找到的str做为第一个参数传到function的第一个参数中,$1,$2,$3第二,三,四个参数中。

现在主要任务就是实现该函数。之后用string. replace(reg,function)就可以一条语句实现。该函数有三个任务:第一个任务就是按名字映射,动态把插值中的name映射到values[name]的值,第二任务是通用格式化,调用Ext.util.Format中的一些函数来格式化通过命名映射找到值,比如trim(values[name])。第三个任务就是调用一些和业务相关的格式转换,比如:把小李变成李先生。该函数如下:

 

var fn = function(m, name, format, args){ 
if(format && useF){//判断有没有format函数或是否允许使用format 
if(format.substr(0, 5) == "this."){ 
//调用自己实现并注册到该Template对象中方法用于业务相关的格式转换, 
return tpl.call(format.substr(5), values[name], values); 
}else{ 
if(args){ //去掉参数中的引号 
var re = /^\s*['"](.*)["']\s*$/; 
args = args.split(','); 
for(var i = 0, len = args.length; i < len; i++){ 
args[i] = args[i].replace(re, "$1"); 
} 
args = [values[name]].concat(args); 
}else{ 
args = [values[name]]; 
} 
//执行Ext.util.Format中的格式转换函数。参数为默认+插值中传入值 
return fm[format].apply(fm, args); 
} 
}else{ 
//命名映射 
return values[name] !== undefined ? values[name] : ""; 
} 
}; 

 在上面有一个地方要注意

 

return tpl.call(format.substr(5), values[name], values); 

 

这个call和fucntion的.call,apply是不一样的。它是调用该类中自己的 call 函数:

 

 function(fnName, value, allValues){ 
return this[fnName](value, allValues); }, 

 

 其作用就是调用this中同名的函数,把value, allValues作为参数传进去。

1.3compile()

接下来我们就是考虑Template的效率问题。模版是重用的,但applyTemplate(Values)每次都要用regexp在模板内容中来插值和replace这些插值,这是一个相当耗时的过程。我们细想一下,在Values设定之前,能不能把这些插值的位置给找到,等Values传进来时就直接把这些位置的置换就可以了。对于string来讲,还是做标识(和插值一样),要么采用性能高效的查询算法,这样的话就用不上regexp。如果要用的话,就提高不了效率了。

有没有更好的办法呢?可以不可以采用分块的思想,把模板内容的string分成若干小部分,比如采用插值的边界来划。这样在数组找到插值的位置就快多了。当替换插值之后,就可以把整个数组给合并成string。而JavaScript数组的join()方式有一个很好的特性,就是对于数组元素不是str,会先转换成string。如是expr, Js 语句会先执行改语句生成string。我们可以充分利用这个特性,把插值部分用Js 语句要替换,等到Values传进来时就调用改数组的join()方法就可以。什么时候传Values值,怎么传呢?这得是由用户说了算,这样最好的办法就是生成一个函数让用户在需要的时候去调用,并传入values。

基本思想已经定了,接下来要做的就是如何去拼凑构建compiled(value)函数了,构建完了,用户就可以调用了。JavaScript的强大灵活之处很大部分就是能动态构建函数。我们需要的函数的形式如下:

 

this.compiled = function(values){ return ['<div style=\”….\”>', 
fm.trim(values['name']),'</div><div style=\”….\”>', fm.toDate(values['birthday']),'</div>'].join('');}; 

 

给定函数的字符串形式,现在就是如何去拼凑了,拼凑完之后通过eval()就变成函数。
下面的代码就是主要拼凑工作:

 

var fn = function(m, name, format, args){ 
if(format && useF){ 
args = args ? ',' + args : ""; 
if(format.substr(0, 5) != "this."){ 
format = "fm." + format + '('; 
}else{ 
format = 'this.call("'+ format.substr(5) + '", '; 
args = ", values"; 
} 
}else{ 
args= ''; format = "(values['" + name + "'] == undefined ? '' : "; 
} 
//',fm.xxxf(values['namex'],xx,yy),' 
//',this.call('xxxf',values['namex'],values),' 
//',(values['namex']==undefined?'':values['namex']),' 
return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'"; 
}; 

我们可以看出它拼成三种字符串的形式,也就上一节提到的三种任务。接下来的: 

body = ["this.compiled = function(values){ return ['"]; 
body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'") 
.replace(this.re, fn)); 
body.push("'].join('');};"); 
body = body.join(''); 

 

 就拼凑成了我们想要的函数字符串。body是字符串的形式,通过eval(body),就动态地生成了compiled(values)这样的函数。之后我们只要调用这个函数就可以了。

1.4小结


Ext.Template还提供了一些其它的方法,用于把生成的模板内容插到Dom Element中,这些很简单。Ext.template完成模板的插值的填充功能,这是模板的最基本的功能,模板还有一部分功能就是指令功能,比如,for指令,if指令等没有实现。

                                                                                                              prk   2008-7-28

分享到:
评论
1 楼 xxf_cz 2008-08-23  
for指令,if指令等 在 Ext.XTemplate 中实现了

相关推荐

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

    6.5.1 Ext.Template 6.5.2 Ext.XTemplate 第7章 设计表单类布局 7.1 Form表单简介 7.1.1 Form表单的基本配置 7.1.2 ExtJS对Form表单的封装 7.2 ExtJS的表单组件 7.2.1 文本输入控件Ext.form.TextField 7.2.2...

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

    6.5.1 Ext.Template 6.5.2 Ext.XTemplate 第7章 设计表单类布局 7.1 Form表单简介 7.1.1 Form表单的基本配置 7.1.2 ExtJS对Form表单的封装 7.2 ExtJS的表单组件 7.2.1 文本输入控件Ext.form.TextField 7.2.2...

    extjs学习资源

    Ext.DomQuery/DomHelper/Template - **DOM查询**: `Ext.DomQuery`提供了类似jQuery的语法来选择DOM元素。 - **模板引擎**: `Ext.Template`允许开发者使用模板字符串创建HTML,并可以方便地插入数据。 ```...

    【Ext2.0】只有月份的日期控件

    这种控件通常适用于那些只关注月份范围,而不涉及具体日期的场景,如统计分析、报表生成等。 在实现上,Ext.ux.MonthPicker.js可能会包含以下关键点: 1. **组件定义**:首先,开发者会定义一个新的类,继承自Ext...

    Ext grid合并单元格

    此案例主要涉及到了自定义 GridView 的 `renderHeaders` 方法来达到单元格合并的目的,并且还涉及了模板(Template)的使用、ColumnModel 的配置以及数据绑定等关键概念。 ### 一、Ext JS Grid 概览 Ext JS 是一个...

    将RowExpander的template换成Gridpanel

    通过分析这个文件,你可以看到具体的实现细节,例如如何配置Grid Panel、Store以及RowExpander的模板。如果你遇到任何问题,可以参考Ext JS的官方文档或者在线社区如iteye上的博文献(如给定的链接),寻求帮助和...

    ext RowExpander 内 加载组件

    template: new Ext.XTemplate( '&lt;p&gt;&lt;b&gt;Full Name:&lt;/b&gt; {name}&lt;/p&gt;', '&lt;p&gt;&lt;b&gt;Email:&lt;/b&gt; {email}&lt;/p&gt;', // 更多字段... ) }], store: store, columns: [...] }); ``` 在上述代码中,`expandColumn: 0`表示在...

    TinyMce For Extjs

    8. `template_list.js`:可能包含了预设的模板列表,用户可以从这些模板中选择,快速填充编辑器内容。 集成TinyMCE到ExtJS的具体步骤包括: 1. 引入所需的ExtJS库和TinyMCE扩展文件。 2. 创建一个ExtJS组件,例如...

    用vc++开发ATL shellext插件.zip

    ATL (Active Template Library) 是Microsoft为C++开发者提供的一套模板库,它主要用于简化COM...通过阅读和分析这些源代码,你可以深入理解shell扩展的实现机制,并可能激发自己去创建更复杂的系统集成解决方案。

    Ext二级联动完整例子

    6. **模板和渲染**:ComboBox的显示样式和内容可以通过模板(Template)进行定制,以满足不同的界面需求。 7. **配置项**:每个ComboBox的配置项可能包括`displayField`(显示字段)、`valueField`(值字段)、`...

    EXT 中文手册 源代码

    通过阅读EXT中文手册的源代码,开发者可以学习到如何配置和使用`grid`组件,如何添加和管理`filter`,以及如何利用`template`高效地生成动态内容。这不仅有助于提高开发效率,也有利于提升应用的用户体验。 在实践...

    extjs4.2选择两个月的日期控件

    这种控件在处理涉及到月份范围的业务场景时非常有用,例如报表生成、预订系统或数据分析等。 EXTJS 4.2的核心是其强大的组件模型,提供了多种预定义的UI控件,包括日期选择器(Date Picker)。默认的日期选择器只能...

    TB+Beast_deZenderphp.ini_dezender_

    6. **TB.php**:可能是一个辅助类或者与 Beast 类相关的工具,TB 可能是 "Template Beast" 或者其他含义。 7. **TB.php.buf.txt** 和 **Beast.php.buf.txt**:可能是临时或备份文件,包含 PHP 代码的原始或处理后的...

    dmde-4-0-6-806-win64-gui.zip

    在数据丢失的情况下,DMDE提供了专业级的数据恢复能力,包括对FAT, NTFS, EXT, HFS+等多种文件系统的支持,同时具备深度扫描和智能分析功能,帮助用户找回丢失的数据。不过,由于数据恢复可能涉及到敏感信息,使用这...

    特思可视化平台

    5. **响应式设计**:考虑到多设备使用场景,平台可能采用了如CE42C2EF9950144E4D89663A8785CC78.cache.html这样的缓存文件来优化加载速度,同时,welcome-nav-template.html和tools.html可能涉及到导航栏和工具页面...

    vue+webpack支持多页面打包

    Webpack 是一个模块打包工具,它将项目中的各种资源(JavaScript、CSS、图片等)视为模块,并根据依赖关系进行静态分析和打包。Webpack 的核心概念包括入口(entry)、输出(output)、加载器(loaders)和插件...

    Flask框架Flask-Login用法分析

    from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker app = Flask(__name__) login_manager = LoginManager() login_manager.init_app(app) engine = create_...

    CI框架集成Smarty的方法分析

    $this-&gt;template_ext = $this-&gt;ci-&gt;config-&gt;item('template_ext'); $this-&gt;caching = $this-&gt;ci-&gt;config-&gt;item('caching'); // 更多配置... } } ``` #### 3. 扩展核心控制器 为Cismarty类编写assign和display...

Global site tag (gtag.js) - Google Analytics