### UI 模块
Tornado 支持一些 UI 模块,它们可以帮你创建标准的,易被重用的应用程序级的 UI 组件。 这些 UI 模块就跟特殊的函数调用一样,可以用来渲染页面组件,而这些组件可以有自己的 CSS 和 JavaScript。
例如你正在写一个博客的应用,你希望在首页和单篇文章的页面都显示文章列表,你可以创建 一个叫做 `Entry` 的 UI 模块,让他在两个地方分别显示出来。首选需要为你的 UI 模块 创建一个 Python 模组文件,就叫 `uimodules.py` 好了:
```
class Entry(tornado.web.UIModule):
def render(self, entry, show_comments=False):
return self.render_string(
"module-entry.html", entry=entry, show_comments=show_comments)
```
然后通过 `ui_modules` 配置项告诉 Tornado 在应用当中使用 `uimodules.py`:
```
class HomeHandler(tornado.web.RequestHandler):
def get(self):
entries = self.db.query("SELECT * FROM entries ORDER BY date DESC")
self.render("home.html", entries=entries)
class EntryHandler(tornado.web.RequestHandler):
def get(self, entry_id):
entry = self.db.get("SELECT * FROM entries WHERE id = %s", entry_id)
if not entry: raise tornado.web.HTTPError(404)
self.render("entry.html", entry=entry)
settings = {
"ui_modules": uimodules,
}
application = tornado.web.Application([
(r"/", HomeHandler),
(r"/entry/([0-9]+)", EntryHandler),
], **settings)
```
在 `home.html` 中,你不需要写繁复的 HTML 代码,只要引用 `Entry` 就可以了:
```
{% for entry in entries %}
{% module Entry(entry) %}
{% end %}
```
在 `entry.html` 里面,你需要使用 `show_comments` 参数来引用 `Entry` 模块,用来 显示展开的 `Entry` 内容:
```
{% module Entry(entry, show_comments=True) %}
```
你可以为 UI 模型配置自己的 CSS 和 JavaScript ,只要复写 `embedded_css`、 `embedded_javascript`、`javascipt_files`、`css_files` 就可以了:
```
class Entry(tornado.web.UIModule):
def embedded_css(self):
return ".entry { margin-bottom: 1em; }"
def render(self, entry, show_comments=False):
return self.render_string(
"module-entry.html", show_comments=show_comments)
```
即使一页中有多个相同的 UI 组件,UI 组件的 CSS 和 JavaScript 部分只会被渲染一次。 CSS 是在页面的 `<head>` 部分,而 JavaScript 被渲染在页面结尾 `</body>` 之前的位 置。
在不需要额外 Python 代码的情况下,模板文件也可以当做 UI 模块直接使用。 例如前面的例子可以以下面的方式实现,只要把这几行放到 `module-entry.html` 中就可以了:
```
{{ set_resources(embedded_css=".entry { margin-bottom: 1em; }") }}
<!-- more template html... -->
```
这个修改过的模块式模板可以通过下面的方法调用:
```
{% module Template("module-entry.html", show_comments=True) %}
```
`set_resources` 函数只能在 `{% module Template(...) %}` 调用的模板中访问到。 和 `{% include ... %}` 不同,模块式模板使用了和它们的上级模板不同的命名 空间——它们只能访问到全局模板命名空间和它们自己的关键字参数。