Otama模版系统

Otama模版系统系统将界面逻辑从模版中完全分离出来. 它是专门为Treefrog框架设计的. 当配置文件(development.ini)按下面这样编辑后使用生成器生成程序骨架时, 将创建Otama系统的视图.

TemplateSystem=Otama

模版完全由HTML写成(使用.html作为后缀名). 一个”标记”用来标识逻辑代码应该插入的元素. 界面逻辑文件(.otm)由C++和标记写成. 当共享的视图(view)库构建时, 这些会被自动转换成C++代码.

视图(View)转换

基本上, 每个操作(action)会生成一对页面逻辑和模版. 文件名为[操作名(action name)].html和[操作名(action name)].otm(大小写敏感). 这些文件保存在”views/controller-name/”文件夹内.

一旦你创建了一个新模版, 为了将这些反馈到视图(view)的共享库中, 你需要在view目录下运行”make qmake”.

$ cd views
$ make qmake

如果没有这样做, 检验在继续这一章时先阅读ERB章节, 因为两个模版系统有很多相同的地方. 还有, 关于Otama系统有很多内容要学习, 先学会ERB将更容易理解Otama.

输出字符串

我们将要输出字符串”Hello world.
” 在用HTML写的模版页, 使用一个叫data-tf的自定义属性, 然后为元素设置一个”标记(mark)”. 属性的名字必须以”@”打头. 举例, 我们这样写:

<p data-tf="@hello"></p>

我们使用段落标签(<p> </p>)包住@hello标记.
在标记中,只可以使用字母和下划线’_’. 不要使用其他字符.

接下来我们来看一些C++代码中的界面逻辑. 我们需要将C++代码和上面使用的b标记关联起来. 我们这样写:

@hello ~ eh("Hello world");

当构建的时候, 运行应用, 视图(view)即将会输出下面的结果:

<p>Hello world</p>

波浪号(~) 通过界面逻辑使用的标记连接C++代码表示”标记元素的内容被取代右侧内容取代”. 记住eh()方法输出的值被传递了.

换句话说, 在P标签中的内容(这个例子为空)被”Hello world”替换了.data-tf属性将完全消失.

此外, 还有一个替代的方法输出同样的内容, 它也可以这样写:

@hello ~= "Hello world"

和ERB中一样, ~和eh()方法的组合可以被写成’~=’, ~和echo()方法的组合可以写成’~==’.

虽然, 为了简单解释, 我们在这里输出了一个静态的字符串, 其实它可以用同样的方式输出一个变量. 当然, 它也可以输出从控制器(controller)传递的对象.

概要: 在想要输出变量的地方放置标记(mark). 然后连接mark到代码.

Otama操作符

夹在C++代码和标记(mark)找那个的符号叫做Otama操作符.

C++代码和元素用Otama操作符结合, 然后决定它们应该如何工作. 在界面逻辑, 要注意Otama操作符的两侧必须有一个空格.

这次, 我们使用一个不同的Otama操作符. 让我们假设逻辑界面是这样写的(冒号).

@hello : eh("Hello world");

视图的结果是这样的:

Hello world

P标签被删除了. 这是因为冒号用结果”更换整个标记(maked)的元素”. 类似上面的内容, 这个也可以这样写:

@hello := "Hello world"

使用从控制器(controller)传递来的对象

为了显示从控制器(controller)导出的对象, 和ERB一样, 你可以用tfetch()宏或者T_FETCH宏获取后再使用它. 当msg可以导出为QString类型的变量时, 你可以这样写:

@hello : tfetch(QString, msg);  eh(msg);

和ERB一样, 获取(fetched)的对象被定义为局部变量.

通常, C++代码不适合写在一行. 要给一个标记(mark)写多行的C++代码, 可以像平常一样写但是要在每行后放置一个空行. 这个空行会被当成一个标记(mark)的一部分. 这样, 在一个标记(mark)和另外一个空行(包括只有一个空字的行)表现为界面逻辑的分割器.

概要:逻辑通过一个空行进行界定.

接下来, 我们看看在两个不同的地方显示一个导出对象的例子. 在这个例子中, 如果你把它写陈#init, 它将首先被获取(fetched). 然后, 它可以在界面逻辑自由的使用.它应该看起来类似这样:

#init : tfetch(QString, msg);
@foo1 := msg
@foo2 ~= QString("message is ") + msg

像说过的那样, 要导出引用多次的对象, 在#init中进行获取(fetch).

这里是另外一种方法导出对象.
在Otama操作符后面放置”$”. 例如, 你可以写成下面这样导出名为obj1的对象.

@foo1 :=$ obj1

它表示obj1对象fetch()处理后, 使用eh()输出值. 然而, 这个处理过程等于获取(fetch)处理, 局部的变量事实上没有被定义.

获得使用echo()方法的输出, 可以这样写:

@foo1 :==$ obj1

和ERB中一样.

概要:要导出对象,使用 =$ 或者~$.

循环

接下来, 我将接收如何使用循环重复显示列表的数字.
在模版中, 我们这样写.

< tr data- tf="@ foreach"> 
  < td data- tf="@ id"></ td>
  < td data- tf="@ title"></ td>
  < td data- tf="@ body"></ td>
</tr>

那些是名为blogList的Blog类的列表导出的对象. 我们像给for语句写一个循环. 这个while语句也是非常类似的.

@foreach :
tfetch(QList<Blog>, blogList);    /* 获取(fetch)处理 */
for (auto &b, blogList) {
     %%
}

@id ~= b.id()

@title ~= b.title()

@body ~= b.body()

%%标记是重要的, 因为它代表标记的整个元素(@foreach). 换句话说, 这个例子中, 它代表从<tr>到</ tr>的元素. 因此, 通过重复<tr>标签, 使用@id, @title, 和@body,设置每个内容元素的值的foreach语句在视图(view)中那个输出这样的结果:

< 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属性将会消失.

增加一个属性

让我们使用Otama操作符给一个元素增加属性.
假设你的模版找那个有这样的标记(mark):

<span data-tf="@spancolor">Message</span>

现在, 假设你在界面逻辑中这样写:

@spancolor + echo("class=\"c1\" title=\"foo\"");

下面是输出的结果:

<span class="c1" title="foo">Message</span>

通过使用+操作符, 你可以增加属性.

顺便提一句, 不可以使用eh()方法代替echo()方法, 因为双引号被转义后会是一个不同的意义.

在界面逻辑中, 我们还可以用另外的一个方法写成这样:

@spancolor +== "class=\"c1\" title=\"foo\""

echo()方法可以重写成’==’.

此外, 还可以写成这样,下面的替代方法输出同样的结果:

@spancolor +== a("class", "c1") | a("title", "foo")

a()方法创建一个表示HTML属性的THtmlAttribute对象, 使用|(竖线)来连接它们. 连接后它不是一个THtmlAttribute对象, 但是如果你用echo()方法输出, 它们会被转换成key1=”val1”, key2=”val2” …这样的字符串, 作为结果添加到属性中. 你愿意的话还可以添加更多属性.

重写<a>标签

可以使用冒号’:’操作符重写<a>标签. 它像上面描述的一样.
回顾一下, <a>标签在模版中是这样标记(marked)的.

<a class="c1" data-tf="@foo">Back</a>

举例, 我们可以这样重写视图(view)(Blog的视图)的界面逻辑:

@foo :== linkTo("Back", urla("index"))

视图(view)输出下面的结果:

<a href="/Blog/index/">Back</a>

因为linkTo()方法生成一个<a>标签, 我们可以得到这结果. 不幸, 类原来的属性已经消失了. 原因就是这个操作符有替换整个元素的效果.

如果你想设置属性, 你可以增加一个参数到linkTo()方法.

@foo :== linkTo("Back", urla("index"), Tf::Get, "", a("class", "c1"))

类属性将会和上面的结果一样被输出.

虽然属性信息可以被输出, 你肯定不会像在界面逻辑中麻烦地重写这些信息.
这里有个解决方法|==操作符. 它有合并标签内容的同时保留属性的效果.

所以, 让我们这样重写界面逻辑:

@foo  |== linkTo("Back", urla("index"))

视图(view)输出下面的结果:

<a class="c1" href="/Blog/index/">Back</a>

原来的类属性保留下来的, 没有消失.

|== 操作符是有条件地合并元素.条件是元素必须是相同的标签.此外, 两者带有相同的属性, 界面逻辑的值优先级更高.

通过使用这个操作符, 设计的信息(HTML属性)可以被转移到模版.

概要: 使用|==操作符保留模版设计的属性然后再合并.

说明:
只有|==操作符才可用, ‘|‘和’|=’都不能工作.

表单标签

不要直接使用表单标签<form>来Post数据, 除非你已经打开跨域访问伪造(CSRF)监测. 只有在模版中写表单标签, 才能接收Post数据. 我们需要用隐藏的参数来嵌入秘密的信息.

我们在模版中使用<form>标签. 将标记(mark)放置在模版的<form>标签后, 然后用formTag()方法的输出作为内容进行合并.

模版:

   :
<form method="post" data-tf="@form">
   :

界面逻辑:

@form |== formTag( ... )

你将可以正常Post数据.

关于打开跨域访问伪造(CSRF)监测和关于安全的更多信息, 请查看安全章节.

擦除元素

如果你在模版中标记@dummy元素, 它不会在视图中输出.假设你的模版这样写.

<div>
<p>Hello </p>
<p data-tf="@dummy">message ..</p>
</div>

然后, 视图(view)将输出下面的结果:

<div>
<p>Hello </p>
</div>

擦除标签

你可以仅保留内容但是删除起始标签和结束标签.

举例, 当使用布局(layout)时, <html>标签会通过布局(laytout)文件输出, 所以你不再需要在模版中输出, 而你希望布局(layout)基于HTML, 而同时将<html>标签保留在模版中.

假设你的模版这样写.

<html data-tf="@dummytag">
<p>Hello </p>
</ html>

然后, 视图(view)将输出下面的结果:

<p>Hello </p>

当你希望在网页设计时保留, 但是在视图中擦除时可以使用它.

包含头文件

我们已经说过关于界面逻辑模版转换到C++代码. 头文件和用户定义的文件不会自动包含, 必须自己添加它们. 不过基本的Treefrog头文件会被包含.

举例, 如果你希望包含user.hblog.h文件, 你应该在界面逻辑的顶部这样写:

#include "blog.h"
#include "user.h"

所有的都和C++代码一样!
以#include开头的字符串自己转移到视图(view)的代码中.

Otama操作符

下面的表描述了我们已经讨论过的Otama操作符.

操作符Operator 描述Description 说明Remarks
: 元素替换
被标记的元素和子元素会完全被操作符右侧的eh()方法或者echo()方法输出的内容替换.
%%表示元素本身可以被替换
~ 内容替换
被标记的元素的内容会被操作符右侧的eh()方法或者echo()方法输出的内容替换.
 
+ 属性增加
如果想更已经标记的元素增加一个属性, 使用这个操作符加上操作符右侧的echo()方法输出的一个字符串.
+= 是HTML转义,可能用得不多.
|== 元素合并
Based on the marked elements, the specified strings will be merged on the right-hand side of this operator.
’|’ 和’|=’ 不可以使用.


这4个操作符的扩展版本在下面. 不再需要echo()语句和eh()语句后, 代码能够写得更短.

| 操作符Operator |描述 Description | |—————————–|——————————————————————————————————————————| | :=
:==
:=$
:==$ |元素被HTML转义的变量替换.
元素被变量替换.
元素被HTML转义的导出的对象替换.
元素被导出的对象替换. | | ~=
~==
~=$
~==$ | 用HTML转义的变量替换内容.
用变量替换内容.
用HTML转义的导出对象替换内容.
用导出的对象替换内容. | | +=
+==
+=$
+==$ |增加一个HTML转义的变量到属性.
增加一个变量到属性.
增加一个HTML转义的导出对象到属性.
增加一个导出的对象到属性. | | |==$ | 用导出的对象合并元素. |


注释

如果你希望在界面逻辑中写注释, 请将注释写在//中.

@foo ~= bar    /*  这里是注释 */

说明: C++ 使用”// ..” 但是这个不能用在界面逻辑中.