高级页面模板1
第九章 高级页面模板
翻译: 杜文山 / 邮件: zope@dohao.org
如有不妥,欢迎批评指正
正文:
在第五章“使用Zope页面模板”里,你学习了页面模板的基础知识。在本章,你将学习高级技术,包括新的表达式类型和宏。
+9.1 高级TAL
你已经学习了一些TAL语句,在这一节,我们将讲述所有的TAL语句,以及它们的各种选项。注意,更为详细的材料请见附录C“Zope页面模板参考”。
+9.1.1高级内容插入
你在第五章已经看到了tal:content和tal:replace是如何工作的。在本节,你将学习内容插入方面的一些高级的技巧。
+9.1.1.1插入结构
通常,tal:replace 和 tal:content语句会忽略HTML标记符,并把文本里的尖括号<转化为<。如果你想插入未经处理的文本,你需要使用带有structure关键字的表达式。例如:
<p tal:replace="structure here/story">
the <b>story</b>
</p>
当你要插入一段存储在一个属性里边或由其他Zope对象生成的HTML或XML时,这个特性是很有用的。例如,你可能有一些新闻条目,这些条目里包含了简单的HTML标记,例如那些显示粗体和斜体的标记。当把它们插入到"Top News"页面里边时,你想保留这些,你就可以写成:
<p tal:repeat="newsItem here/topNews"
tal:content="structure newsItem">
A news item with<code>HTML</code> markup.
</p>
这样就会把带有HTML标记符的新闻条目插入到段落里边。
+9.1.1.2模拟元素(Dummy Elements)
通过使用内建的变量nothing,你可以包含模板里可见的页面元素,而在生成的文本里却不可见。比如:
<tr tal:replace="nothing">
<td>10213</td><td>Example Item</td><td>$15.34</td>
</tr>
这个功能用于填充最终要被动态内容替换掉的部分。例如,一个具有10行的表格在模板里通常只有1行。通过添加9行模拟行,模板的样子就更像最终的结果了。
并不总是需要在你的页面模板里使用tal:replace="nothing"机制来加入模拟内容。例如,你已经看到了一些tal:content 或 tal:replace元素内的内容往往在执行后被删掉。在这种情况下,你无须进行任何特定功能来确信模拟内容已经删除掉了。
+9.1.1.3默认内容
通过在tal:content 或 tal:replace里使用default表达式,你可以保留标记符的内容。例如:
<p tal:content="default">Spam<p>
这也就意味着:
<p>Spam</p>
大多数的时候,你需要有选择性的包含默认的内容,而不总是包含它。例如:
<p tal:content="python:here.getFood() or default">Spam</p>
注意::Python表达式在本章的后边阐述。如果getFood方法返回一个真值,那么就就把结果插入到段落里,否则使用默认的Spam。
+9.1.2高级标记符重复
你已经在第五章看到了使用tal:repeat语句通常情况下可以完成什么任务。本节侧重于讲述一些这个语句的高级特性。
+9.1.2.1重复变量
特别值得一提的是重复变量。重复变量提供了当前重复的相关信息。重复变量有以下属性:
- index - 重复的序号,从0开始
- number - 重复的序号,从1开始
- even - 对于偶数序号(0, 2, 4, ...)为真。
- odd - 对于奇数序号(1, 3, 5, ...)为真。
- start - 对于起始重复为真(index 0)。
- end - 对于结尾或最终的重复为真
- length - 序列长度,就是重复总次数
- letter - 用小写字母计次,"a" - "z", "aa" - "az", "ba" - "bz", ..., "za" - "zz", "aaa" - "aaz"等等。
- Letter - 用大写字母计次。
你可以使用路径表达式或Python表达式来访问重复变量的内容。在路径表达式里,由三部分组成,即repeat名称,语句变量名称和你要的信息名称,例如repeat/item/start。在Python表达式里,使用通常的字典方式就可以得到重复变量,然后就可以访问信息属性,例如:
'python:repeat['item']<a class="new" href="http://members.czug.org/zope/zopebook/X_e9_ab_98_e7_ba_a7_e9_a1_b5_e9_9d_a2_e6_a8_a1_e6_9d_bf1/createform?page=%27item%27" title="create this page">?</a>.start'
+9.1.2.2重复提示
这里是一些有用的提示。有些时候,你希望重复一个标记符,但不要合拢标记符。例如,你可能想重复几个段落标记符,但不需要在另外一个标记符里合拢他们。你可以通过使用tal:omit-tab语句实现这个功能:
<div tal:repeat="quote here/getQuotes"
tal:omit-tag="">
<p tal:content="quote">quotation</p>
</div>
tal:omit-tag语句在本章后边进行描述。
尽管前边已经提到过,值得再提一次::你可以嵌套tal:repeat语句。每个tal:repeat语句必须有一个不同的重复变量名。以下是一个显示数学乘方表的例子:
<table border="1">
<tr tal:repeat="x python:range(1, 13)">
<div tal:repeat="y python:range(1, 13)"
tal:omit-tag="">
<td tal:content="python:'%d x %d = %d' % (x, y, x*y)">
X x Y = Z
</td>
</div>
</tr>
</table>
这个例子使用了Python 表达式,其中的tal:omit-tag语句在本章后边进行进一步讲述。
如果你已经用过很多次DTML里边的dtml-in重复语句,你会已经遇到了批块化。批块化就是把一个大的列表分隔为多个小列表的过程。典型的是用它来在一个web页面里显示一个大列表里的小列表项目。就比如搜索引擎如何批块化显示搜索结果。tal:repeat语句不支持批块化,但是Zope带有一个批块化工具。见本章后边的“批块化”部分。
tal:repeat另外一个没有提供的有用的特性是排序。如果你想对一个列表排序,你或者编写自己的排序脚本(在Python里是相当容易的),或者你可以使用sequence.sort工具函数。以下是一个如何按照标题对一个列表排序,然后按照修改日期排序的例子:
<table tal:define="objects here/objectValues;
sort_on python:(('title', 'nocase', 'asc'),
('bobobase_modification_time', 'cmp', 'desc'));
sorted_objects python:sequence.sort(objects, sort_on)">
<tr tal:repeat="item sorted_objects">
<td tal:content="item/title">title</td>
<td tal:content="item/bobobase_modification_time">
modification date</td>
</tr>
</table>
这个例子试图通过在sort函数外边定义sort参数。在这个例子里,如何对序列排序的描述是在sort_on变量里定义的。关于sequence.sort函数的更多信息请参见附录B“API参考”。
+9.1.3 高级属性控制
你已经用到了tal:attributes语句。你可以用它来动态替换标记符属性,例如,对于a 元素的href属性。通过用分号分隔开属性,你可以对一个标记符替换多个属性:
<a href="link"
tal:attributes="href here/getLink;
class here/getClass">link</a>
你还可以用XML名称空间定义属性,例如:
<Description
dc:Creator="creator name"
tal:attributes="dc:Creator here/owner/getUserName">
Description</Description>
简单的把XML名称空间前缀放在属性名称前面,你可以用XML名称空间创建属性。
+9.1.4 定义变量
通过使用tal:define属性,你可以定义你自己的变量。这样做有几个原因。一个原因是要避免在模板里重复编写长的表达式。另外一个原因就是要避免重复的调用复杂的方法。一旦你定义了变量,你就可以在一个模板里多次使用。例如,下边是一个定义了变量的列表,后边测试它,并对它进行重复:
<ul tal:define="items container/objectIds"
tal:condition="items">
<li tal:repeat="item items">
<p tal:content="item">id</p>
</li>
</ul>
tal:define语句创建了变量items,你就可以在任何地方的ul标记符里使用它。还要注意的是,在同一ul标记符里是如何使用两个TAL语句的。有关如何对一个标记符使用多个语句方面的信息,请见本章后边的“TAL语句之间的交互”部分。在这个例子里,第一个语句分配变量items,然后第二个语句在一个条件里使用它,并判断是否为真值。如果items为假值,那么ul标记符不显示。
现在,假设当没有条目时并不是简单的删去列表,而是显示一条消息。要实现这一点,在列表前加入以下代码:
<h4 tal:condition="not:container/objectIds">There
Are No Items</h4>
当container/objectIds为假时,表达式not:container/objectIds为真,依次类推。参见本章后边的“Not 表达式”部分。
此时你还不能使用变量items,因为他还没有定义。如果你把items定义移到h4标记符前边,那么你不能在ul标记符里使用,这是因为它变成了h4标记符的本地变量。你可以把定义放置在包括h4和ul标记符的其他标记符里,但是有一种简单的解决方法。通过在变量名称前放置关键词global,你就可以在定义它的h4标记符到模板底部之间使用它:
<h4 tal:define="global items container/objectIds"
tal:condition="not:items">There Are No Items</h4>
你可以通过用分号分隔开,使用tal:define定义多个变量。例如:
<p tal:define="ids container/objectIds;
title container/title">
你可以定义任意多个变量。每个变量可以由它自己的全局或本地范围。你还可以在后边定义里引用前边的定义。例如:
<p tal:define="title template/title;
tlen python:len(title);">
通过使用tal:define,你可以增进模板的效率和可读性。
+9.1.5 忽略标记符
你可以删除带有tal:omit-tag语句的标记符。你会很少使用这个语句,但是有时候还是有用的。omit-tag属性删除一个标记符,但不影响标记符内容。例如:
<b tal:omit-tag=""><i>this</i> stays</b>
执行后为:
<i>this</i> stays
这种用法,tal:omit-tag操作就像是tal:replace="default"。然而,当tal:omit-tag与其他TAL语句(比如::tal:repeat)一起使用时,就显得更为有用。例如,这里是一种用tal:repeat创建10个段落标记符的用法:
<span tal:repeat="n python:range(10)"
tal:omit-tag="">
<p tal:content="n">1</p>
</span>
这样就会产生10个段落标记符,然而,span标记符并不会在输出里显示。
tal:omit-tag属性能够用表达式,尽管通常你只要用空表达式就行了。如果表达式为真,或没有表达式,那么语句标记符被删除。如果表达式为假,那么标记符不被忽略。这样就允许你根据动态情况有选择性的删除标记符。
+9.1.6 错误处理
如果在你的页面模板里发生了错误,你可以捕捉这个错误,并给用户显示一条有用的消息。例如,假设你的模板定义了一个使用表单数据的变量:
...
<span tal:define="global prefs request/form/prefs"
tal:omit-tag="" />
...
如果Zope遇到了一个错误,比如在表单数据里不能找到prefs变量,整个页面将终止;而你将得到一个错误页面。值得庆幸的是,通过使用tal:on-error语句以及错误处理机制,你就可以避免这种事情:
...
<span tal:define="global prefs here/scriptToGetPreferences"
tal:omit-tag=""
tal:on-error="string:An error occurred">
...
当执行模板时引发了一个错误,那么Zope就会查找tal:on-error语句来处理这个错误。它先在当前的标记符里查找,然后在合拢的标记符里,就这样一直到顶级的标记符。当它找到一个错误处理器,它用错误处理表达式替换标记符内容。在这个例子里,span标记符将包含一个错误消息。
一般情况下,你将对一个标记符定义错误处理器,其中包含逻辑页面元素,例如表格。如果一个错误影响了绘制表格,那么错误处理器可以从页面里忽略表格,或者用某种错误消息替换它。
对于更为灵活的错误处理,你可以调用脚本。例如:
<div tal:on-error="structure here/handleError">
...
</div>
任何发生在div里的错误将调用handleError脚本。注意structure选项允许脚本返回HTML。你的错误处理脚本可以检测错误,并且根据错误的类型采取不同的处理方法。你的脚本访问错误是通过名称空间调用error变量。例如:
## Script (Python) "handleError"
##bind namespace=_
##
error=_['error']<a class="new" href="http://members.czug.org/zope/zopebook/X_e9_ab_98_e7_ba_a7_e9_a1_b5_e9_9d_a2_e6_a8_a1_e6_9d_bf1/createform?page=%27error%27" title="create this page">?</a>
if error.type==ZeroDivisionError<a class="new" href="http://members.czug.org/zope/zopebook/X_e9_ab_98_e7_ba_a7_e9_a1_b5_e9_9d_a2_e6_a8_a1_e6_9d_bf1/createform?page=ZeroDivisionError" title="create this page">?</a>:
return "<p>Can't divide by zero.</p>"
else
return """<p>An error occurred.</p>
<p>Error type: %s</p>
<p>Error value: %s</p>""" % (error.type,
error.value)
你的错误处理脚本可以采取各种处理方法,例如,它可以通过发送邮件记录错误。
tal:on-error语句并不意味着一般的例外处理。例如,你不能用in验证表单输入。你应该使用脚本,这是因为脚本允许你完成强大的例外处理。tal:on-error语句适合于处理执行模板时所发生的错误。
+9.1.7 在TAL语句之间交互
当每个元素中只有一个TAL语句时,执行的顺序是简单的。从root元素开始,执行每个的元素语句,然后访问每个下级元素,按照这个顺序,执行他们的语句,依次类推。
可是,存在相同的元素拥有多个TAL语句的情况。除了tal:content和tal:replace语句不能结合在一起外,任何语句的结合都可能出现在相同的元素里边。
当一个元素有多个语句时,他们的执行顺序如下:
- define
- condition
- repeat
- content or replace
- attributes
- omit-tag
由于tal:on-error语句只有当发生错误时才出现,因此,它没有列出来。
采用这种顺序的原因是::因为一般先要设置其他语句里使用的变量,因此define放在第一位。接下来要做的事情是决定是否包含这个元素,因此condition放在其次,并且还由于condition可能依赖于刚才设置的变量,因此,它放在define后边。能够用每次循环的值替换元素的各个部分是很有价值的,因此repeat 放在content replace 和 attributes前面。Content和replace 不能同时对同一元素应用,因此它们处于同一位置。Omit-tag位于最后,这是因为没有其他的语句依赖于它,并且它应该位于define和repeat后边。
以下是一个包含多个TAL语句的例子:
<p tal:define="x /root/a/long/path/x | nothing"
tal:condition="x"
tal:content="x/txt"
tal:attributes="class x/class">Ex Text</p>
注意tal:define语句是如何先被执行的,其他的语句依赖于它的结果。
当对元素结合TAL 语句时,你有三个应该知道的限制:
- 对于单一的标记符,每一种语句当中只能应用一个。这是因为HTML不允许相同名称的属性出现多次出现。例如,你不能在同一标记符中出现两个tal:define。
- tal:content 和 tal:replace不能同时在同一标记符中出现,这是因为它们的功能是相反的。
- 标记符中编写TAL属性的顺序不影响他们执行的顺序。不管你如何排列它们,TAL语句执行总是按照上边所描述的固定顺序执行。
如果你打破TAL语句的顺序,你必须在另外一个元素里合拢这个元素,可能是div或span,以及对这个新元素放入一些语句。例如,假设你想对一个项目序列进行循环,但要跳过一些。下边的例子试图编写一个模板,它从0循环到9,并跳过3:
<!-- broken template -->
<ul>
<li tal:repeat="n python:range(10)"
tal:condition="python:n != 3"
tal:content="n">
1
</li>
</ul>
这个模板不会工作,这是因为在执行重复以前先检测条件。以下是解决这个问题的一种方式:
<ul>
<div tal:repeat="n python:range(10)"
tal:omit-tag="">
<li tal:condition="python:n != 3"
tal:content="n">
1
</li>
</div>
</ul>
这个模板通过在一个合拢的div标记符中定义n变量。注意,由于存在tal:omit-tag语句,div标记符不会在输出里显示。这种方式可能不好,但确实管用。也许将来版本里的页面模板会以一种更好的方式解决这个问题。
