内置模板事件

Finch 在每次 Jinja 渲染时,都会向模板中注入一组内置变量和辅助函数。这些内容可以通过特殊前缀访问,与你自己通过 addParam() 添加的数据并存。

全局变量

{{ isLocalDebug }}   {# 调试模式下运行时为 true #}
{{ data }}          {# 通过 rq.addParam() 添加的所有参数组成的 map #}
{{ session }}       {# 服务器端会话(session)map #}
{{ $rq }}           {# Request 对象 #}

资源辅助方法

{{ assets.js() }}      {# 渲染所有通过 rq.addAsset() 添加的 <script> 标签 #}
{{ assets.css() }}     {# 渲染所有 <link> 标签 #}
{{ assets.dataJs() }}  {# 渲染供 JS 访问的 data 属性 #}

路由和 URL 辅助方法($e)

{{ $e.route }}                              {# 匹配到的路由的渲染后路径,例如 '/users/42' —— 即匹配此次请求的具体 path/extraPath #}
{{ $e.routePath }}                          {# 路由的规范完整路径模式,例如 '/users/{id}'(始终基于主 `path`,即便实际匹配的是某个 `extraPath`) #}
{{ $e.routeKey }}                           {# 路由的 `key`,例如 'users.show' —— 与 $e.route 并不是同一个东西 #}
{{ $e.isKey('root.panel') }}                {# 当前路由的 key 是否匹配 #}
{{ $e.hasKey(['root.panel', 'root.form']) }} {# 当前路由的 key 是否是其中之一 #}

{{ $e.routeUrl('key') }}                           {# 具名路由的 URL #}
{{ $e.routeUrl('users.show', {'id': '42'}) }}       {# 带路径参数的 URL #}
{{ $e.routeUrl('search', {}, {'q': 'hello'}) }}     {# 带查询参数的 URL #}

{{ $e.uri }}            {# 完整请求 URI,以经过百分号编码的字符串形式返回 #}
{{ $e.uriString }}      {# 完整请求 URI,未经编码 #}
{{ $e.path }}           {# 仅请求路径,以经过百分号编码的字符串形式返回 #}
{{ $e.pathString }}     {# 仅请求路径,未经编码 #}
{{ $e.isPath('/example/form') }}  {# 当前路径是否匹配 #}
{{ $e.endpoint }}       {# 匹配到的端点路径 #}

{{ $e.url('/about') }}                    {# 根据路径构建绝对 URL #}
{{ $e.urlParam('/users', {'id': '5'}) }}   {# 追加查询参数后的 URL #}
{{ $e.urlToLanguage('fa') }}              {# 将当前 URL 切换为另一种语言 #}

尽管名字看起来如此,$e.uri/$e.path 实际上是经过百分号编码的字符串,并不是 Dart 的 Uri 对象,也不是路径段列表——如果需要原始的、未编码的值,请使用 $e.uriString/$e.pathString。

{{ $e.getCookie('theme', 'light') }}  {# 读取一个带默认值的 cookie;此读取始终是明文(未加密)的 #}

Language Helpers

{{ $e.ln }}            {# 当前语言代码,例如 'en' #}
{{ $e.langs }}         {# 所有已配置语言的列表:[{code, label, contry}] #}

$e.dir 以及 $e.langs 中每个条目里的 label/contry 字段,并不是从语言文件中一个普通的 "dir" 键读取的——它们查找的是专门的翻译键,命名为 language.<code>_dir、language.<code>_label 和 language.<code>_contry(注意:是 contry,不是 country——这是框架本身真实存在的拼写问题,并非本文档的笔误)。如果这些键在你的语言文件中不存在,.tr.write() 会静默地回退,直接返回该键本身,因此 {{ $e.dir }} 实际渲染出来的会是字面文本 language.en_dir,而不是 ltr。

Finch 示例项目自身的语言文件只定义了一个普通的 "dir" 键,并没有定义这些 language.* 键,因此开箱即用时 $e.dir/$e.langs[].label/$e.langs[].contry 都是无法解析的。示例项目自身的布局通过改用 {{ $t('dir') }}(直接查找普通的 "dir" 键)绕开了这个问题——参见 Templates 中的完整示例。你可以自行为每个语言文件添加 language.<code>_dir/_label/_contry 这些键,也可以优先使用 $t('dir') 或你自己的翻译键,而不是 $e.dir 以及 $e.langs 的 label/contry 字段。

工具辅助方法

{{ $e.widgetPath('partials/nav') }}   {# 为路径追加所配置的 widget 扩展名,例如 'partials/nav.j2.html' —— 不会自动添加 widgetsPath 前缀 #}
{{ $e.randomString(8) }}             {# 生成一个 8 个字符的随机字符串(若省略参数,默认长度为 4) #}
{{ $e.toString(value) }}             {# 将任意值转换为字符串 #}

翻译

{{ $t('logo.title') }}                          {# 翻译一个键 #}
{{ $t('greeting', {'name': user.name}) }}       {# 带参数翻译 #}

嵌套数据导航

{{ $n('user/address/city', 'Unknown') }}  {# 安全地导航嵌套参数 #}

调试转储

{{ dump(data) }}   {# 在浏览器中可视化转储任意变量(仅调试模式) #}

自定义本地事件

在 app.dart 中使用 Request.localEvents 定义你自己的全局函数:

Request.localEvents.addAll({
  'currentYear': () => DateTime.now().year,
  'appName': () => 'My App',
});

在模板中使用 $l. 前缀访问它们:

<footer>© {{ $l.currentYear() }} {{ $l.appName() }}</footer>