入門編のこの記事の内容、サンプルコードは Nunjucks に書いてある内容を参考・一部引用し作成している
テンプレート継承周りの話は別に書く予定なので、この記事では関連するテンプレートタグにも触れていない
Nunjucks は Python のテンプレートエンジン Jinja2 に影響を受けているテンプレートエンジンで、Mozilla が継続的にメンテナンスをしている
記法は EJS に近く、HTML テンプレートに {% set greeting = "HELLO" %}
のように用意されている独自タグを使うことができる
EJS よりもテンプレートエンジンとして役にたつ機能が多数用意されているが、記法が JavaScript とも違っている書き方になっているもの多い
テンプレートエンジンを触ってこなかった人にとってはとっつきにくいと感じる人が多いかもしれない
HTML テンプレート中で式や文(テンプレートタグ)を書くことができる
式は {{ username }}
のように中括弧2つで囲うことで展開され、テンプレートタグは {% set username %}
のように中括弧と %
で囲うことで実行される
{% set username = "joe" %}
{{ username }} {# username が展開される}
もちろん文字列だけではなく、配列やオブジェクトも定義できる
{% set arr = [1,2,3,4,5] %}
{% set obj = { a : 1, b : 2, c : [0, 1, 2, {d : 3}]} %}
JavaScript のようにプロパティの値にドットや角括弧([]) の記法を使ってアクセスできる
{{ profile.name }}
{{ profile["name"] }}
値が undefiled
または null
の場合、何も表示されない
undefined
または null
オブジェクトを参照する場合も同じように何も表示されない
{# profile が未定義だった場合 #}
{{ profile }} {# 何も表示されない #}
{{ profile.firstName }} {{ profile.lastName }} {# 何も表示されない #}
式のあとに縦棒(|
)を追加して、 {{ foo | join(",") }}
のように書くと foo
の値を join
という関数へ渡し、その実行結果を表示することができる フィルター という機能が使える
フィルターは自分で用意することもできるが、 Nunjacks がビルトインで便利なフィルターを多数用意してくれている
以下はビルトインで入っているフィルターを使用した例
{# 文字列を整数へ変換する #}
{{ '100' | int }}
{# => 100 #}
{# 改行コードを `<br>` へ変換する #}
{{ 'こんにちは\n明日は晴れです' | nl2br }}
{# => こんにちは<br>明日は晴れです #}
{# 配列を引数で渡した値で結合する #}
{{ [0, 1, 2, 3, 4] | join('-') }}
{# => 0-1-2-3-4 #}
{# オブジェクトの値を `JSON.stringify` した結果を表示する #}
{# デバッグしたいときに有用 #}
{% set items = { a : 1, b : [1, 2, 3, c : { d : 123 }] } %}
{{ items | debug }}
{# => {"a":1,"b":[1,2,3,{"c":5}]} #}
{# HTML 文字列(具体的には &, <, >, ‘, ”)をエスケープできる #}
{{ "<html>" | escape }}
{# => <html> #}
{# 特定のキーを基準に配列をまとめ直す #}
{% set items = [
{ name : '田中' , type : '赤組' },
{ name : '花澤' , type : '白組' },
{ name : '伊藤' , type : '赤組' },
{ name : '川田' , type : '白組' }
] %}
{% for type , items in items | groupby ("type" ) %}
<p ><strong >{{ type }}</strong > :
{% for item in items %}
{{ item.name }}
{% endfor %} </p >
{% endfor %}
{# =>
<div>
<p><strong>赤組</strong> :
田中
伊藤
</p>
<p><strong>白組</strong> :
花澤
川田
</p>
</div>
#}
条件分岐するにはテンプレートタグ if
が使える
{% if 条件式 %}
~ {% endif %}
でブロックを作って HTML テンプレートを囲うことで条件に一致したものが表示される
{% if greeting %} {# もし `greeting` が truthy だったら #}
こんにちは!
{% else %}
あいさつ文が登録されていません
{% endif %}
elseif
を使って条件文をネストさせることも可能
{% if greeting %} {# もし `greeting` が truthy だったら #}
こんにちは!
{% elif happy %}
幸せです!
{% else %}
よろしくおねがいします!
{% endif %}
{# 配列のループ #}
{% set arr = [1, 2, 3] %}
{% for value in arr %}
<p >{{value}}</p >
{% endfor %}
{# =>
<p>1</p>
<p>2</p>
<p>3</p>
#}
{# 辞書型のループ #}
{% set obj = {a : 1, b : 2, c : 3} %}
{% for key , value in obj %}
<p >{{key}}, {{value}}</p >
{% endfor %}
{# =>
<p>a, 1</p>
<p>b, 2</p>
<p>c, 3</p>
#}
{% for x , y , z in [[0, 1, 2], [3, 4, 5], [6, 7, 8]] %}
<p >Point: {{ x }}, {{ y }}, {{ z }}</p >
{% endfor %}
{# =>
<p>Point: 0, 1, 2</p>
<p>Point: 3, 4, 5</p>
<p>Point: 6, 7, 8</p>
#}
ループの中で loop
という変数を使える
loop.index
: ループ中の現在のインデックス値(1から)
loop.index0
: ループ中の現在のインデックス値(0から)
loop.revindex
: ループが終わるまでの残り回数(1まで)
loop.revindex0
: ループが終わるまでの残り回数(1まで)
loop.first
: 最初の値かどうかを真偽値(boolean)で返す
loop.last
: 最後の値かどうかを真偽値(boolean)で返す
loop.length
: アイテムの数
カスタムテンプレートローダーを使う場合に使う
asyncEach
(順序を保持しない), asyncAll
(順序を保持する)
再利用可能なテンプレートを macro
というタグで定義できる
{% macro MacroName %}
~ {% endmacro %}
で任意の macro 名を指定し、 テンプレート囲い macro
を定義する
{% macro field (name , value ='' , type ='text' ) %}
<div class =" field" >
<input type =" {{ type }}" name =" {{ name }}"
value =" {{ value | escape }}" />
</div >
{% endmacro %}
定義したテンプレート内で {{ field('user') }}
のように実行する
引数を渡したり、デフォルト値をセットすることができる
import
を使えば外部ファイルに定義した macro
を読み込んで再利用することができる
{% import "forms.html" as forms %} {# forms.html に定義している macro へ forms という namespace を通してアクセスできるようにする #}
{{ forms.field('user') }}
現在の namespace 上へ import することもできる
利用するものが限られているのであればこちらの方法を
{% from "forms.html" import field , label as description %}
{# 現在の namespace 上に field macro を import #}
{# 現在の namespace 上に label macro を description という名前で import #}
include
タグを使って外部テンプレートを読み込むことができる
{% include "header.html" %}
include は for
ループの中でつかうことができたり、文字列だけではなく式を渡すこともできるので、下のように動的なパス指定をすることもできる
{% for item in items %}
{% include item + "item.html" %}
{% endfor %}
テンプレートが読み込めなかったときにスルーさせるには ignore missing
オプションを追加する
{% include "item.html" ignore missing %}
中括弧 {{}}
などを書いたまま出力させたい場合に使う
{% raw %}
{{hoge}}
{% endraw %}
{# => {{hoge}} #}
ブロックの内容を filter へ渡す - filter
先述で紹介した filter は式の後に 縦棒(|)
を追加して filter へ値を渡していた
filter タグを使ってブロックを作ると、中のテキストをまるっと filter に渡すことができる
{% filter replace ("いい天気" , "豪雨" ) %}
今日はいい天気だな。
明日もいい天気だといいな。
{% endfilter %}
{# => 今日は豪雨だな。 明日も豪雨だといいな。 #}
返ってくるのはあくまで、文字列なので注意
タグが含まれていても文字列として返ってくる
中のテキストを外部ファイルで定義していて、全体に何か処理をかけたいときには便利かも
{% filter replace ("いい天気" , "豪雨" ) %}
{% include "./sample.njk" %}
{% endfilter %}
ブロックの内容を macro へ渡す - call
{% macro add (x , y ) %}
{{ caller() }}: {{ x + y }}
{% endmacro %}
{% call add (1, 2) -%}
The result is {# このブロックの内容を macro 内で `caller()` を実行して受け取れる #}
{% - endcall %}
{# => The result is: 3 #}
デフォルトではスペースは書いた通り表示される
開始ブロックの後ろ、終了タグの前にハイフン( -)を追記することで余白を詰めた状態で出力させることができる
{% for i in [1,2,3,4,5] %}
{{ i }}
{% endfor %}
{# =>
1
2
3
4
5
#}
{% デフォルトではすべての空白をそのまま出力する %}
{% for i in [1,2,3,4,5] -%}
{{ i }}
{% - endfor %}
{# => 12345 #}
{% if numUsers < 5 %} ...{% endif %}
{% if i == 0 %} ...{% endif %}
and
or
not
カッコを使用して式をグループ化する
{% if users and showUsers %} ...{% endif %}
{% if i == 0 and not hideFirst %} ...{% endif %}
{% if (x < 5 or y < 5) and foo %} ...{% endif %}