Otama
模板系统的最大特点在于表现层的纯粹性,使用原生的HTML
标签取代形如<%...%>
这样的嵌入脚本代码。
使用之前,记得把配置文件(development.ini
)中的模板系统设置为Otama
:
TemplateSystem=Otama
Otama
模板文件的后缀名是最一般的.html
,区别于ERB
模板的.erb
,同时它把所有的逻辑代码全部转移到一个独立的文件中,它的后缀是.otm
,虽然它是纯C++
写的。我们可以这么理解,html文件
和otm文件
相结合等同于ERB
系统的erb文件
。
一般来说每个动作都对应有一套表现逻辑和模板文件,它们的命名分别为动作名.otm
和动作名.html
,它们被放在views/控制器名/
目录下。
每当你新创建了一个模板文件,记得要重新编译,因为程序运行时使用的是编译之后的共享库文件。
$ cd views
$ make qmake
1.输出字符串
如果我们想往网页输出Hello world
,可以对特定的标签元素添加data-tf
属性,它的值必须是@
开头,并且只能用字母和下划线。比如在模板文件中(.html文件
)中这样写:
<p data-tf="@hello"></p>
然后在表现逻辑文件(.otm文件
)中对应地有:
@hello ~ eh("Hello world");
那么最终生成返回给用户的页面将会是这样的:
<p>Hello world</p>
波浪线(~
)的意思是,用右边表达式的返回值去填充data-tf属性值
为左边的网页标签,然后data-tf属性
则会完全消失。而eh()
方法就是返回经过HTML转义
得到的字符串。
为了实现同样的效果,我们也可以这么写:
@hello ~= "Hello world"
正如ERB
模板系统一样,~
和eh()
函数的组合可以简写为~=
;相似地,~
和echo()
函数的组合也可以简写为~==
。
但是右边不一定非得一定是字面常量,完全可以是一个普通变量甚至是一个对象实例。
2.Otama运算符
联系模板文件中的“标记属性”和表现逻辑代码文件中的变量的是Otama
运算符,例如上面的~
或者~=
。
如果这一次我们选择使用另一个运算符——:
,例如
@hello : eh("Hello world");
那么最终生成返回给用户的页面将变成这样:
Hello world
<p>标签
被完全移除了,因为:
运算符会用右边的值完全替代拥有对应标记属性
的网页元素。正如~
和eh()
可以简写为~=
,:
和eh()
也可以简写为:=
。
@hello := "Hello world"
3.使用控制器传过来的对象实例
正如在ERB
中的那样,为了使用控制器的导出变量,我们必须先调用tfetch()
或者T_FETCH()
宏定义。例如,如果这个变量属于QString
类型,那么我们可以这么写:
@hello : tfetch(QString, msg); eh(msg);
正如在ERB
中的那样,通过这种方式获取的变量相当于一个局部变量,注意它的作用范围——从fetch()
开始到遇到一个空行。
接下来,我们来研究下如何在两个地方使用同一个导出变量。这种情况下,我们可以通过#init
来实现预抓取。这样,在此之后都可以随意使用它,用法如下:
#init : tfetch(QString, msg);@foo1 := msg@foo2 ~= QString("message is ") + msg
其实还有另一种使用导出变量的方法——利用$
这个特殊运算符。比如,你可以通过下面的代码直接输出导出变量的值:
@foo1 :=$ obj1
这相当于tfetch()
和eh()
的结合,如果想要实现tfetch()
和echo()
相结合,可以这么写:
@foo1 :==$ obj1
4.实现循环
如果我们想输出一个博客文章列表,那么必不可少地要用到循环。首先,我们要在模板文件中像这样写:
<tr data-tf="@foreach"><td data-tf="@id"></td><td data-tf="@title"></td><td data-tf="@body"></td>
</tr>
然后在表现逻辑文件的内容如下:
@foreach :tfetch(QList<Blog>, blogList); /* Fetch processing */for (auto &b, blogList) {%%}@id ~= b.id()@title ~= b.title()@body ~= b.body()
其中最重要的就是%%
这个符号,它指代的是整个带@foreach
属性值的标签元素,比如在上面的例子中就是<tr>
到</tr>
。
这样,for(auto %b,blogList){}
中的每一次循环都会输出一个<tr>...</tr>
结构块。
<tr><td>100</td><td>Hello</td><td>Hello world!</td>
</tr><tr><td>101</td><td>Good morning</td><td>This morning ...</td>
</tr><tr>: (← 不断重复)
同理,data_tf
这个属性会消失不见。
5.动态增加属性
我们可以使用Otama
运算符来对网页元素动态增加指定属性,例如我们有这样一个标记元素:
<span data-tf="@spancolor">Message</span>
现在,假设我们这样写表现逻辑的代码:
@spancolor + echo("class=\"c1\" title=\"foo\"");
那么,最后我们得到的页面将会是:
<span class="c1" title="foo">Message</span>
在这里,+
运算符就实现了增加属性的功能。同时,我们也可以这么写:
@spancolor +== "class=\"c1\" title=\"foo\""
因为echo()
等同于==
,或者还可以这么写:
@spancolor +== a("class", "c1") | a("title", "foo")
这里的a()
方法会创建一个带属性的HTML
超链接标签,同时可以使用|
来连接它们。但是如果你在这里使用echo()
方法来代替==
,那么最终的输出结果将会变成这样的字符串——key1=”val1”, key2=“val2”……
。
6.生成超链接
举个例子,如果我们的模板文件内容如下:
<a class="c1" data-tf="@foo">Back</a>
然后,表现逻辑代码文件的内容如下:
@foo :== linkTo("Back", urla("index"))
那么,最终生成的页面代码将会是:
<a href="/Blog/index/">Back</a>
因为:运算符会完全完全替代原来的网页元素,然后linkTo(内容,连接地址)
方法又会生成一个新的<a>
标签。如果你想保留原来模板中的<a>
标签的属性,而不是完全替换,可以这么写:
@foo :== linkTo("Back", urla("index"), Tf::Get, "", a("class", "c1"))
如果你不想重复写两次class="c1"
,也可以考虑使用|==
运算符。它会合并前后两组属性从而生成一个新的标签,比如:
@foo |== linkTo("Back", urla("index"))
最终生成的网页代码为:
<a class="c1" href="/Blog/index/">Back</a>
值得注意的是,如果两个地方都出现了同一个属性,那么将优先取值为表现逻辑代码的。
7.生成表单
使用表单进行POST
数据时,最好开启CSRF
(跨站请求伪造)的检查功能。生成<form>
的做法跟生成<a>
有多少相似的地方,比如我们会在模板文件这样写:
<form method="post" data-tf="@form">
然后在表现逻辑这么写:
@form |== formTag( ... )
8.隐藏一个网页元素
我们可以使用@dummy
标记属性来隐藏一个网页元素,比如:
<div><p>Hello</p><p data-tf="@dummy">message ..</p>
</div>
最终生成的页面将会是:
<div><p>Hello</p>
</div>
9.删除标签而保留内容
有时候,你真的需要这个功能,比如布局文件已经输出了<html>
元素,然后你在模板端就不需要重复在输出,但是仍需要保留<html>...</html>
内部的内容,那么可以这么写:
<html data-tf="@dummytag"><p>Hello</p>
</html>
最终输出结果如下:
<p>Hello</p>
10.导入头文件
因为表现逻辑代码是由C++
写成的,所以必不可少地要导入相应的头文件。我们可以这样导入我们自己编写的头文件:
include "blog.h"
include "user.h"
但是一些基础的TreeFrog
框架头文件会自动导入,我们无需再多此一举。