つい最近知ったのですが、ftlにも名前空間があります。
例えば、ある画面にマクロを定義しているFTLを複数読み込む時、そのFTL毎に名前空間を分ける、ということができます。
名前空間の使い方
これの何がいいかというと、名前空間を分けると各FTLで定義した変数名やマクロ名が衝突しなくなります。library-a.ftl
で foo
というマクロを定義し、 library-b.ftl
でも同名のマクロを定義し、この2つのFTLを同じ画面で読み込み、それぞれのマクロを同時に、別のマクロとして使うことができるようになります。
<#-- library-a.ftl -->
<#macro copyright date>
<p>Copyright © $(date) John Smith. All rights reserved.</p>
</#macro>
<#assign mail = "jsmith@acme.com">
上記のライブラリFTLを lib/library-a.ftl
とします。
そして、このライブラリFTLを top.ftl
で利用します。
<#inclde "/lib/library-a.ftl">
このように書くと、 copyright
マクロと mail
変数がグローバルな名前空間に定義されてしまいます。
これだと、仮に top.ftl
ですでに copyright
という名前のマクロや mail
という変数を定義していると、名前が衝突してエラーになってしまいます。(変数は同じものとして扱われます)
これだと、再利用可能なコードとは言えません。
そこで、 import
という、別の構文を使ってライブラリFTLを読み込みます。
<#-- top.ftl -->
<#import "/lib/library-a.ftl" as libA>
<@libA.copyright date="2012-2013"/>
${libA.mail}
<#-- 出力内容
<p>Copyright © 2012-2013 John Smith. All rights reserved.</p>
jsmith@acme.com
-->
import
を使ってライブラリFTLを読み込むと、新しい名前空間が作成されます。
この新しい名前空間からは、ライブラリFTL内で定義された変数か、グローバルで定義された変数か、データモデルの変数(Javaのアクションクラスから渡された変数)しか見ることができません。
この新しい名前空間にある2つの変数(マクロ)に top.ftl
からアクセスするには、 import
構文で as
の後ろに指定された名前を使います。この場合、上のコードで示したように、 libA.copyright
, libA.mail
という形で library-a.ftl
で定義された変数やマクロにアクセスできます。
このように import
構文を使ってFTLを読み込むと、たとえ top.ftl
で同名の変数やマクロが定義されていたとしても衝突してエラーにはなりません。
別の名前空間内の変数を編集する
<#-- top.ftl -->
<#import "/lib/library-a.ftl" as libA>
${libA.mail}
<#assign mail="jsmith@other.com" in libA>
${libA.mail}
<#-- 出力内容
jsmith@acme.com
jsmith@other.com
-->
データモデルと名前空間
データモデルの変数はどこからでも参照できます。
例えば user
という名前の変数がデータモデルにあった場合、lib/library-a.ftl
はこの user
変数にアクセスできます。
<#-- library-a.ftl -->
<#macro copyright date>
<p>Copyright © $(date) ${user}. All rights reserved.</p>
</#macro>
<#assign mail = "${user}@acme.com">
user
が"Fred"だった場合、下記のようになります。
<#import "/lib/library-a.ftl" as libA>
<@libA.copyright date="2012-2013"/>
${libA.mail}
<#-- 出力内容
<p>Copyright © 2012-2013 Fred. All rights reserved.</p>
Fred@acme.com
-->
ただし、名前空間内でデータモデルと同名の変数が宣言されていた場合、後者が勝ちます。
名前空間のライフサイクル
同じFTLを別名で複数回 import
で読み込んだ場合、名前空間は最初の読み込みに対してのみ作られ、以後の読み込みは同一の名前空間に対する別名となります。
<#import "/lib/library-a.ftl" as libA>
<#import "/lib/library-a.ftl" as foo>
<#import "/lib/library-a.ftl" as bar>
${libA.mail}, ${foo.mail}, ${bar.mail}
<#assign mail="jsmith@other.com" in my>
${libA.mail}, ${foo.mail}, ${bar.mail}
<#-- 出力内容
jsmith@acme.com, jsmith@acme.com, jsmith@acme.com,
jsmith@other.com, jsmith@other.com, jsmith@other.com,
-->
グローバル変数とローカル変数
FTLにもグローバル変数とローカル変数があります。
assign(ローカル変数)
<#assign foo="bar">
変数の定義や置き換えができます。
特定の名前空間内に変数を作成したり、置き換えることもできます。
(<#assign someValue = "foo" in someNamespace>
)
assign
を利用して宣言された変数は、名前空間が変わると参照できません。
global(グローバル変数)
<#global foo="bar">
global
を使って定義された変数は、すべての名前空間で参照できます。 しかし、個々の名前空間内で同名の変数が定義されていると、その名前空間からはグローバル変数の方にはアクセスできません。
ただし、その場合でも globals
という特別な変数を介してアクセスすることはできます。
${.globals.foo}
globals
を介してグローバル変数にアクセスできるだけでなく、データモデルの変数にもアクセスすることができます。
local(ローカル変数)
<#local foo="bar">
local
構文はマクロや関数の中でのみ使用することができます マクロ/関数内で local
を利用して作られた変数は、そのマクロ/関数外からは参照できません。
つまりどうしたらいい?
- ライブラリFTLを読み込むときは
import
で読み込む - マクロから参照したい変数が親FTLにある場合、
global
を使って宣言する - マクロや関数内では、特に意図した場合でない限り
local
を使って変数宣言する
参考
FreeMarker Manual - Namespaces
FreeMarker Manual - local
FreeMarker Manual - assign
FreeMarker Manual - global