模板语法

FarBox的基本模板引擎是建立在HTML基础上的Jinja2框架。
但你可以同时使用Jinja2(.html文件后缀)与Jade(.jade文件后缀)语法。

Jinja2语法

基本语法

FarBox的模板语法就是普通的HTML,但可以使用{{}} {%%}嵌入特殊的API代码。

{{ 使用变量或者运行函数 }}
{% 逻辑运算 %}

逻辑代码

如果循环、判断,则会有对应起止,示例如下:

{% if site.title %}我有名字{% endif %}  --> 如果没有 {% endif %} 会报错

{%for post in posts %}
    文章的标题: {{post.title}}
{% endfor %} --> 如果没有{% endfor %} 也会报错

除了{% if %}、{% endif %}、{% for %}、{% endfor %}这些基本逻辑运算外,这几个也是常见的:{% block %}、{% endblock %}、{% extends 'something.html' %}、{% include 'something.html' %} 实现模板之间的继承、引用。

模板结构

模板的继承(extends)

父模板(比如名为base.html),它负责的是一个基本的框架,

Title Here
{% block head %}{% endblock %} 
{% block content %}{% endblock %}

子模板(比如名为detail.html),它继承父模板的框架,并最终渲染页面

{% extends "base.html" %}  # 这里申明从哪个父模板继承
{% block content %}  # 声明block
    具体的内容
{% endblock %}
    **没有在block里面的内容,最终不会呈现,比如我这一行文字。**

{% block abc %}
    在base.html中,并没有声明一个abc的block,所以,这段文字也是不会被呈现。
{% endblock %}

使用Jade的语法可以写成

extends base
block content
    具体的内容
**没有在block里面的内容,最终不会呈现,比如我这一行文字。**        
block abc
    base.html中,并没有声明一个abc的block,所以,这段文字也是不会被呈现。

模板的引用(include)

比如名为t1.html的源码如下:

1234567
{% include 't2.html' %}
89

t2.html的源码如下:

我是嵌入的文字或者代码

那么最终呈现为:

1234567
我是嵌入的文字或者代码
89

模板文件的路径

比如有以下的目录结构(注意实际使用过程中,不要出现以中文为名的文件(或文件夹)

|--FarBox
    |-- template(目录)
        |-- base.html
        |-- index.html
        |-- post.html
        |-- includes(目录)
            -- comments.html
        |-- sub_folder(目录)
            -- sub_index.html

不论在sub_index.html或者index.html申明继承base.html,都应该是{% extends 'base.html'%}.

comment.html如果想要被引用的话,则要是这样的路径{% include 'includes/comments.html' %}

简单的说,继承、引用的模板路径是针对于template这个文件而言的相对路径。

sub_folder.sub_index是等效于sub_folder/sub_index.html的,也同样等效于sub_folder/sub_index.jade; base则等效于base.htmlbase.jade

FarBox实际模板路径

sub_folder/a_template.html sub_folder/a_template.jade, 作为核心模板,在FarBox的系统中,实际上会被转译为.sub_folder.a_template, 所以两者是完全等价的。

Jade语法

FarBox将我们常见的文件目录结构转化为了可快速查询的数据库调用,而FarBox API的主要功能在于渲染页面(HTML)。

如果用传统的角度看待,那么,一个是后端,一个是前端。所以,我们一直在寻找可以融合前后端的API语法,经过改造的Jade语法,基本上满足了我们的设计需求。

Jade是Node.js世界中的一种Python式缩进的模板语法,经过改造后,FarBox可以将.jade文件转化为Jinja2的语法。

为了更好地进行编写,你可能需要为自己的代码编辑器/IDE增加Jade语法的支持插件。

HTML标签、id、class

.class_name this is content
// 默认会创建div标签,最终结果为<div class="class_name">this is content</div>

tag_name.class_name
// 最终结果为<tag_name class="class_name"></tag_name>

p.class1.class2.class3
// 多个class,最终结果为<p class="class1 class2 class3"></p>

a.a_class(href="#", title="this is a link")
// 多个属性,用`,`隔开,最终结果为<a class="a_class" href="#" title="this is a link"></a>

span#dom_id.class_name_1.class_name_2 this is content
// 最终结果为<span id="dom_id", class="class_name_1 class_name_2">this is content</span>

.class_name this is line 1
| this is line 2
//
     | 可以在跨行中使用,最终结果为<div class="class_name">
        this is line 1
        this is line 2
    </div>

变量与HTML标签

如果直接将具体的变量与HTML标签关联起来,可参照如下语法。

node post.content  
//等同于<node>post.content</node>

var = post.content
// 等同于 {%set var = post.content %}, 是一个变量的赋值

node= post.content 
// =号左前无空格,则是将变量转为HTML标签,等同于<node>{{ post.content }}</node>

node= 'post.content'
// 等同于<node>post.content</node>

如果HTML标签中有其它属性定义的。

a(href=post.url)= post.title
// 等同于 <a href={{post.url}}>{{post.title}}</a>

a(href='/post/{{post.url_path}}')= post.title
// 这混合了Jinja2语法,等同于 <a href='/post/{{post.url_path}}'>{{post.title}}</a>

for/if

head
    if site.title == 'test'
        title= site.title
    elif site.title == 'a test too'
        title= site.title + '!!!'
    else
        title= 'blank'

body
    for post in posts
        h1= post.title

等效于

<head>
    {% if site.title=='test' %}
            <title>{{ site.title }}</title>
    {% elif site.title =='a test too' %}
        <title>{{ site.title + '!!!' }}</title>
    {% else %}
        <title>blank</title>
    {% endif %}
</head>
<body>
    {% for post in posts %}
        <h1>{{ post.title }}</h1>
    {% endfor %}
</body>

注意!!

不要将if/else/for跟其它逻辑混合, 以下语法都是错误的,会导致模板编译无法成功进行。

if post
    post.content
else no content 
// 这样写的else是无效的,既不是HTML标签,也不是逻辑判断; 要空一行先。

运行函数

+raise_404()
// 前面增加+号,则不会被转译为HTML的标签,最终的结果为{{ raise_404() }}
// 这只是举个例子,raise_404是一个特殊函数,实际上,不加+号也无妨;
// 但其它普通函数或者你自己定义的函数,则必须使用+号来声明。

Jade的继承与引用

extends与include

这分别对应Jinja2语法中的extendsinclude,但跟Jinja2不一样的是,不要增加单引号/或双引号。

extends base
extends base.jade
// 等价于 {% extends 'base.jade' %}

include include/comments
include include/comments.jade
include include.comments.jade
//
    等价于{% include 'include/comments.jade' %}
    我们可以使用`.`代替`/`,为避免冲突,必须保证有文件后缀名,比如 include include.comments 是错误的语法

macro/mixin

macro 是Jinja2的概念,mixin则是Jade的概念; 在FarBox API中两者基本等价。

macro相当于自定义一个函数,然后在模板中进行调用,另外,我们也支持对这个函数产生的数据进行缓存,前提是使用cache_为开头的函数名。

Jinja2是Python语言创建的模板框架,所以macro的定义方式,基本接近Python语言中函数的定义。

mixin sidebar(name)
    h1= name
    p this is sidebar

// 等价于
{% macro sidebar(name) %}
    <h1>{{ name }}</h1>
    <p> this is sidebar </p>
{% endmacro %}

如果macro的定义与调用,都在同一个模板页,则后面可以直接使用这个定义出来的函数。

如果处于不同模板页面,则要先行import;这是Jade语法中不存在的概念,所以要使用Jinja2语法。

比如

{% from "mixins.jade" import get_url %}

{# 注意`mixins.jade`需要使用双引号或者单引号包围 #}

macro相对于include而言,它的性能其实更高,include不可避免地会带入一个变量空间,从而造成混乱的概率增加;而macro的内容如果不调用外部变量,则可以有效地防止变量空间混乱的情况。

Jade与Jinja2语法融合

常见例子

在.jade的模板文件中,可以在单独一行中使用Jinja2的语法。

extends base
{% from "mixins" import make_posts_list %}
{{ site.title }}

.entry
    | {{ post.title }} -
    | {{ post.date }}

{# 以上这样的写法是能够正式解析的 #}

变量关键词

'post', 'posts', 'site', 'sites', 'files', 'tags', 'tag', 'folder', 'folders','category', 'images', 'albums', 'album', 'next_one', 'pre_one', 'paginator', 'pager',这些作为变量的关键词,不能直接作为HTML的Tag处理。

举例说明

/*这样是有效的,会解析为<div>{{post.content}}</div>*/
div
    post.content


/*doc不是被保护的关键词,会被解析为<div><doc class="content"></doc></div>*/
div
    doc.content

/* 如果需要doc也能同post变量一样工作,则要写成*/
div= doc.content
/* 或者 */
div
    | {{ doc.content}}

函数关键词

'load', 'set_content_type', 'redirect', 'a_with_selected', 'set_per_page', 'add_doc_actions' 这些FarBox系统函数,则可以在单行中直接使用,不会被Jade特别转译为HTML代码。

'need_login', 'need_admin', 'raise_404' 这些函数在Jade语法中,不论是need_login() 还是need_login这样的写法,最终都会被解析为{{ need_login() }}

Jade的自动补全

FarBox对.jade后缀的模板文件进行编译过程中,会进行以下的自动补全:

  • 如果没有声明doctype,则默认补全为doctype html
  • 如果是html类型的doctype,但没有申明feed地址,默认会补全feed标签,其地址对应/feed
  • 如果是html类型的doctype,但没有声明charset的,默认补全为utf8