整理Qweb基础语法,翻译自 https://www.odoo.com/documentation/13.0/reference/qweb.html
QWeb是Odoo使用的主要模板引擎。它是XML模板引擎,主要用于生成HTML片段和页面。
引出 <t> 标签
模板指令被指定为以t-为前缀的XML属性,例如,用于条件判断的t-if,其元素和其他属性将直接呈现。有时候为了避免呈现元素,还提供了占位符元素<t>,该元素执行其指令,但不会自身生成任何输出。
当时用<t>标签时,如果这里的 condition 是 True
<t t-if="condition">
<p>Test</p>
</t>
将会得到结果渲染到页面:
<p>Test</p>
而当使用其他标签(常规html标签,非 <t> 标签),如果这里的 condition 是 True
<div t-if="condition">
<p>Test</p>
</div>
将会得到结果渲染到页面:
<div>
<p>Test</p>
</div>
<t> 标签就是用来干这事的。
输出指令
Qweb 有两个输出指令,一个主要的输出指令 t-esc ,和一个 伪输出指令 t-raw 。两者的区别:t-esc 输出的是数据库的原始值,t-raw ,不会对特殊符号转义,会将内容里的标签一起渲染到页面。
实践为证,执行SQL,查看数据库里存了个啥:
select e_link from zerone_book where id = 1
看到结果:
分别使用 t-esc 和 t-raw 输出:
<t t-esc="item.e_link"/>
<t t-raw="item.e_link"/>
渲染结果:
条件判断指令
Qweb使用 t-if 做条件判断。
<div>
<t t-if="condition">
<p>ok</p>
</t>
</div>
如果条件 condition = True,会得到以下结果渲染到页面:
<div>
<p>ok</p>
</div>
如果条件 condition =False,会得到以下结果渲染到页面:
<div>
</div>
条件渲染适用于指令的载体,而该载体不必是<t>,得到的渲染结果是一样的:
<div>
<p t-if="condition">ok</p>
</div>
另外,还提供了附加的条件分支指令t-elif和t-else:
<div>
<p t-if="user.birthday == today()">Happy birthday!</p>
<p t-elif="user.login == 'root'">Welcome master!</p>
<p t-else="">Welcome!</p>
</div>
循环指令
QWeb 有一个迭代指令 t-foreach,用来接受一个返回集合进行迭代的表达式。它还有个指令 t-as,用来表示迭代的“当前项”的名称:
<t t-foreach="[1, 2, 3]" t-as="i">
<p><t t-esc="i"/></p>
</t>
得到渲染结果:
<p>1</p>
<p>2</p>
<p>3</p>
像条件判断指令一样,t-foreach 适用于指令的载体,而该载体不必是<t>,得到的渲染结果是一样的:
<p t-foreach="[1, 2, 3]" t-as="i">
<t t-esc="i"/>
</p>
t-foreach 可以在数组(当前项为当前值)或映射(当前项为当前键)上进行迭代。
属性
QWeb可以即时计算属性,并在输出节点上设置计算结果。 这可以通过t-att(属性)指令完成,该指令以3种不同形式存在:
t-att-$name
创建一个名为$name的属性,计算属性值并将结果设置为属性值:
<div t-att-a="42"/>
<div t-att-style="color:red;"/>
得到渲染结果如下:
<div a="42"/>
<div style="color:red;"/>
t-attf-$name
和前面一样,但参数是一个格式字符串,而不仅仅是表达式,通常用于混合文字和非文字字符串:
<t t-foreach="[1, 2, 3]" t-as="item">
<li t-attf-class="row style_{{item}}">
<t t-esc="item"/>
</li>
</t>
得到渲染如下:
<li class="row style_1">1</li>
<li class="row style_2">1</li>
<li class="row style_3">1</li>
注 * 在 t-attf-* 指令中使用上下文变量时,可以使用双花括号 {{ }} 进行字符串的拼接
t-att=mapping
如果参数是映射,则每个(键,值)对都会生成一个新属性及其值:
<div t-att="{'a': 1, 'b': 2}"/>
得到渲染如下:
<div a="1" b="2"></div>
t-att=pair
如果参数是对(2个元素的元组或数组),则对的第一项是属性的名称,第二项是值:
<div t-att="['a', 'b']"/>
得到渲染结果如下:
<div a="b"></div>
设置变量
QWeb允许在模板中创建变量,记忆一个计算(多次使用),给一块数据起一个更清晰的名字等。
通过指令 t-set 实现,一般情况下,赋值使用 t-value ,像下面这样:
<t t-set="foo" t-value="2 + 1"/>
<t t-esc="foo"/>
将会在对应dom位置渲染出 3 。
在不使用 t-value 的情况下,将会渲染节点的主体,并将其设置为变量的值,像这样:
<t t-set="foo">
<li>ok</li>
</t>
<t t-esc="foo"/>
在对应dom位置,渲染出 <li>ok</li> , <li>ok</li> 就相当于 t-value 的值。
调用子模板
QWeb模板可用于顶层渲染,但也可以使用指令 t-call 在另一个模板中使用它们(以避免重复或为模板的各个部分命名)
一个 template 好像只会渲染第一个 <t t-name=""> ,在我亲测中(odoo13),如果像下面这样:
<template id="template1">
<t t-name="old-template">
<p>template-1</p>
</t>
<t t-name="new-template">
<p>template-2</p>
<t t-call="old-template"/>
</t>
</template>
得到渲染结果:
<p>template-1</p>
只在页面中,渲染出第一个 <t t-name="old-template"> 的内容,后面的 <t t-name = 'new-template' > 被忽略掉了。
所以,我将它们分别写到不同的 template 里,我在odoo13中,测试 t-call 的使用,t-call 的值应是 被调用子模板所在的template的 id ,代码如下:
<template id="template1">
<t t-name="old-template">
<p>template-1</p>
</t>
</template>
<template id="template2">
<t t-call="模块名.template1"/>
<t t-name="new-template">
<p>template-2</p>
</t>
</template>
字段
t-field 此指令 不能 在 t 标签中使用
# 此接口返回3条最新新闻
@http.route('/news/top/3')
def zerone_retrun_3_top_news(self, **kwargs)
recs = http.request.env["zerone.news"].sudo().search([], order="id desc", limit=3)
return http.request.render('zerone.new3_news',{'news':recs})
<template id="new3_news">
<t t-foreach="news" t-as="item">
<a t-att-href="item.href"><p t-field="item.title"/></a>
</t>
</template>
渲染出:
<a href="url1"><p>title1</p></a>
<a href="url2"><p>title2</p></a>
<a href="url3"><p>title3</p></a>
t-options 可用于自定义字段,最常见的选项是小部件,其他选项依赖于字段或小部件。
定义模板
使用 t-name 伪指令只能放在模板文件的顶层(将子级直接指向文档根目录):
<templates>
<t t-name="template-name">
<!-- template code -->
</t>
</templates>
模板继承
模板继承用于改变现有的模板,例如,将信息添加到其他模块创建的模板中。
模板继承是通过t-extend指令执行的,该指令以要更改的模板名称作为参数。
当t-extend与t-name结合使用时,将创建一个具有给定名称的新模板。
在这种情况下,扩展模板不会更改,相反,伪指令定义了如何创建新模板。
在这两种情况下,都可以使用任意数量的t-jquery子伪指令执行更改:
<t t-extend="base.template">
<t t-jquery="ul" t-operation="append">
<li>new element</li>
</t>
</t>
t-jquery指令采用CSS选择器。 在扩展模板上使用此选择器来选择要应用指定t操作的上下文节点。
- append 节点的主体附加在上下文节点的末尾(在上下文节点的最后一个子节点之后)
- prepend 节点的主体位于上下文节点之前(在上下文节点的第一个子节点之前插入)
- before 节点的主体被插入到上下文节点之前
- after 节点的主体被插入到上下文节点之后
- inner 节点的主体替换了上下文节点的子节点
- replace 节点的主体用于替换上下文节点本身
- attributes 节点的主体应该是任意数量的属性元素,每个属性元素都具有name属性和一些文本内容,上下文节点的named属性将设置为指定的值(如果已经存在则替换为该值,或者如果不存在则添加)
其他
t-log 接受一个表达式参数,在渲染过程中计算该表达式,并使用console.log记录其结果:
<t t-set="foo" t-value="42"/>
<t t-log="foo"/>
将输出42
t-debug 在模板渲染期间触发调试器断点:
<t t-if="a_test">
<t t-debug="">
</t>
如果调试处于活动状态,则将停止执行(具体条件取决于浏览器及其开发工具)。
t-js 节点的主体是在模板渲染期间执行的javascript代码。 带有一个上下文参数,该名称是渲染上下文在t-js主体中可用的名称:
<t t-set="foo" t-value="42"/>
<t t-js="ctx">
console.log("Foo is", ctx.foo);
</t>
最后最后,如果有同学发现那里不对,在下方留言,贴出你的demo ,因为 odoo 官方文档挺老了,我能动手的确实有限。