Personal tools
You are here: Home Zope Zope宝典 高级页面模板1
Document Actions

高级页面模板1

by 潘俊勇 last modified 2005-12-14 14:25

第九章 高级页面模板

翻译: 杜文山 / 邮件: 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语句不能结合在一起外,任何语句的结合都可能出现在相同的元素里边。

当一个元素有多个语句时,他们的执行顺序如下:

  1. define
  2. condition
  3. repeat
  4. content or replace
  5. attributes
  6. 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 语句时,你有三个应该知道的限制:

  1. 对于单一的标记符,每一种语句当中只能应用一个。这是因为HTML不允许相同名称的属性出现多次出现。例如,你不能在同一标记符中出现两个tal:define。
  2. tal:content 和 tal:replace不能同时在同一标记符中出现,这是因为它们的功能是相反的。
  3. 标记符中编写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标记符不会在输出里显示。这种方式可能不好,但确实管用。也许将来版本里的页面模板会以一种更好的方式解决这个问题。


Powered by Plone CMS, the Open Source Content Management System

This site conforms to the following standards: