这些例子当中,ITemplate都不是显式声明的。我们仅仅是在xml-script中制定了以某一个DomElement为基础生成一个ITemplate,但是ITemplate不是Control,到底ITemplate是如何生成的呢?
在PreviewScript.js中,我们可以看到唯一一个实现了ITemplate的类:Template(全称为Sys.Preview.UI.Template),它有一个parseFromMarkup的静态方法,这个方法用于解释xml-script中的<layoutTemplate />段落,然后根据其中的layoutElement属性给出的DomElement生成Template的实例。
如果要单纯用JavaScript指定基于某个DomElement生成ITemplate,那该怎么做呢?在官方论坛上有一张名为How to implement the Sys.UI.ITemplate interface programmatically的帖子,里面有人给出了在CTP中可行最简单的答案,放到Beta1中代码应该是这样的:
var layoutElement = $get("layoutElement");
var templateInstance = Function.emptyMethod;
templateInstance.createInstance = function() {
return {instanceElement:layoutElement};
}
这样你就获得了一个名为templateInstance的对象,它基于HTML中一个id为"layoutElement"的节点。templateInstance可以用于DragDropList.set_dropCueTemplate,虽然它连ITemplate都没继承。
给出了这个例子以后,大家肯定就要问为什么可以这样做。其实客户端的ITemplate和服务器端的ITemplate是很类似的,它们的功能都是关联到一个声明性模板,并且在特定时刻将声明性模板的内容转换为树提供给上级容器使用。只不过客户端的ITemplate在createInstance时生成DomElement树;服务器端的ITemplate则在InstantiateIn时生成控件树(这个以后在《深入理解 ASP.NET 动态控件》里再分析)。要生成DomElement树这就再简单不过了,只需要直接从整个DOM中摘取你想要的节点不就是咯,所以就有了上述解决方案。
不过这不是一个太好的解决方案,首先它没做到继承自ITemplate,如果ITemplate的使用方先检查你给的对象是否继承真的自ITemplate,那么templateInstance对象就无法通过检查。其次它直接返回DOM上面的节点,而createInstance会被调用多次,无论是单个调用方还是多个调用方的情形,调用方都会期望每次调用返回一个新的DomElement树,然而templateInstance每次给的都是同一个,这就可能造成一些奇怪的问题。
为了解决这两个问题,我们需要设计一个新的Template类,实现ITemplate并且保证createInstance时每次都返回新的树(使用cloneNode),完整的代码我放在这里:继承自Control的简单Template。
最后就是提问时间,你觉得这个Template类继承自Control是好还是不好?好与不好我都各列举一个原因作为范例:
- 好的地方是它采用类似Control的构造函数,可以直接用$create来从DomElement创建Template。
- 不好的地方是它破坏了Template的语意,因为从语意上来说Template和Control应该是互斥的。一个东西是Template就不应该是Control,它的功能是为容器提供DomElement/Control,它的角色应该是provider。
那么你觉得到底是好还是不好呢?
没有评论:
发表评论