Velocityでテンプレートの継承
Djangoが好きな理由の一つに、テンプレートの柔軟性があります。
特に、継承ができるところ、これは慣れると継承のないテンプレートエンジンでは物足りなさを感じてしまいます。
今回はSAStruts+Velocityの組み合わせで、テンプレートの継承はできないか探したところ、少し古いですが以下の記事を見つけました。
Velocityでテンプレートを継承する方法 - Random Note(
継承の利点についてもここに書かれている通りです。
Velocity Layout Servletもありますが、Djangoになれると継承の方がわかりやすいんじゃないかな?
特に構造が重要なHTMLなんかは多重継承的なことができれば構築も楽になり、保守もしやすいんじゃないかと思います。
早速、上記の記事で公開されていたソースを使って継承を試してみました。色々試してみたいので備忘録。
今回はEclipse上でDoltengプロジェクトを作り、そこにVelocityを入れていきます。
プロジェクトの用意
EclipseにDoltengを入れてある前提です。
まずDoltengプロジェクトを新規作成。
名前はVelocityExtendsとかにします。
プロジェクトのファセットは
を選びました。
Velocityの準備
まずVelocityをインストールします。
この辺を参照。STUDY-NET おぼえがき -【JAVA】SAStrutsとVelocityの連携
ちなみに、ビルドパスにはcommons-digester-1.6.jarが既にありますので、1.8を入れた後はビルドパスから外して削除してしまいましょう。
VelocityExtendsを入れる
Velocityの動作が確認できたら、先ほどの記事で配られていたソースを適切な場所に配置します。
これは、Velocityに対するユーザでディレクティブという形で実装されているようです。
ソースのパッケージ宣言を見ると
org.apache.velocity.runtime.directive
となっていますので、src/main/java 以下に、このパッケージを作成し、5つのソースを入れます。
次にvelocity.propertiesに実装したユーザディレクティブを加えるように、宣言を書きます。
velocity.propertiesをいじる際にはプロパティーエディターが必要です。よくわからなかったら一応ググっておくといいでしょう。
all in oneのeclipseとか使ってて、velocity.propertiesが緑色のアイコンになってれば多分入っているので大丈夫です。
書くのは以下の二つ。
#------------------------------- # USER DIRECTIVE #------------------------------- userdirective=org.apache.velocity.runtime.directive.Extends userdirective=org.apache.velocity.runtime.directive.ExtendBlock
これでエラーが出なければ、継承は動くはずです。
そのままだと書きにくいので、ツールも設定します。
ツールの設定
vmファイルをいじるためのツールは、eclipseの場合は、
- velocity UI
- veloeclipse
等があります。
HTMLの取り回し的には、veloeclipseの方が良さそうなので、veloeclipseを使っています。
こちらから落とせます。http://propsorter.sourceforge.net/veloeclipse
どちらでも設定は同じだったと思います、要するに今回作成したユーザディレクティブをツールは知らないので、
きちんと教えてあげないと、構文のエラーと認識してしまうということです。
ユーザディレクティブをツールに認識させるには、
[ウィンドウ]-[設定]-[Veloeclipse]-[ユーザディレクティブのリスト]に新規で以下を加えます。
- extends(ブロック)
- block(ブロック)
これで、認識されるはずです。
最初はうまく認識されませんでしたが、eclipse自体を再起動したらうまく認識されました。
あと、この辺はこちらも参考になります。
Velocity用Eclipse plugin・Veloeclipseの導入とStruts2対応 - 文殊堂
継承
では早速使ってみます。
HTMLのテンプレートを作り、必要な部分だけ上書きします。
common/base.vmを以下のように作成します。
次にindex.jspをindex.vmにし、base.vmを継承してみます。
base.vm
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>#block('title') base title #end</title> </head> <body> <h1>#block('h1') base h1 #end</h1> <div> #block('contents') <p>this is contents of base</p> <p>you can over write here by extends</p> #end </div> </body> </html>
次にこれを継承するようにindex.vmを変更します。
継承したいソースは、/WEB-INF/からの絶対パスじゃないとうまくいきませんでした。
これ相対にならないのか、そのへんは探ってるところです。
index.vm
#extends('/WEB-INF/view/common/base.vm') #block('title') index title #end #block('h1') index h1 #end #block('contents') <p>this is contents of index</p> <ul> <li>Velocity</li> <li>meets</li> <li>extends</li> <li>like</li> <li>Django</li> </ul> #end #end
結果
アクセスします。
http://localhost:8080/VelocityExtends/
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title> index title </title> </head> <body> <h1> index h1 </h1> <div> <p>this is contents of index</p> <ul> <li>Velocity</li> <li>meets</li> <li>extends</li> <li>like</li> <li>Django</li> </ul> </div> </body> </html>
実際のソースには空行とか入ってしまっているようですが、構造的にはちゃんと意図したとおりになっています。
継承しなかった場所は、もとの記述内容がそのままレンダリングされます。
#extends('/WEB-INF/view/common/base.vm') #block('title') index title #end #block('h1') index h1 #end #end
必要なところだけ上書きできるということです。
段階的な継承
さっきのbaseをまずbase2.vmで継承
base2.vm
#extends('/WEB-INF/view/common/base.vm') #block('contents') <p>this is contents of index</p> <ul> <li>Velocity</li> <li>meets</li> <li>extends</li> <li>like</li> <li>Django</li> </ul> #end #end
それをindexが継承、このときbaseのtitleやh1を継承できる。
index.vm
#extends('/WEB-INF/view/common/base2.vm') #block('title') index title #end #block('h1') index h1 #end #end
継承項目の追加
こちらで言及されていますが、できませんでした。
追記:Velocityと継承 - logiqboard
何がしたいかというと、
base2.vm
#extends('/WEB-INF/view/common/base.vm') #block('contents') <p>this is contents of index</p> <ul> <li>Velocity</li> <li>meets</li> <li>extends</li> <li>like</li> <li>Django</li> </ul> #block('ex_contents') <p>ここにさらに継承できる項目を追加</p> #end #end #end
でこれを継承します。
index.vm
#extends('/WEB-INF/view/common/base2.vm') #block('title') index title #end #block('h1') index h1 #end #block('ex_contents') 追加項目をindex で継承してみます。 #end #end
結果
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title> index title </title> </head> <body> <h1> index h1 </h1> <div> <p>this is contents of index</p> <ul> <li>Velocity</li> <li>meets</li> <li>extends</li> <li>like</li> <li>Django</li> </ul> </div> </body> </html>
継承できないどころか、デフォルトの内容も反映されません。
多重継承
ここで言ってるのは、二つのテンプレートを持ってきたいということです。
たとえばbase と base2 を index で継承してみます。
baseはそのまま。
base2.vm
新しくブロックを足します。
<div> <p>here is block of base2.vm</p> #block('subcontents') <p>this is subcontents of base2</p> #end </div>
この二つを継承します。
index.vm
#extends('/WEB-INF/view/common/base.vm') #block('title') index title #end #block('h1') index h1 #end #extends('/WEB-INF/view/common/base2.vm') #block('subcontents') <p>index subcontents</p> #end #end #end
結果は
<div> <p>here is block of base2.vm</p> </div> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title> index title </title> </head> <body> <h1> index h1 </h1> <div> <p>this is contents of base</p> <p>you can over write here by extends</p> </div> </body> </html>
継承の順番がわかってきました。最初に#endが終わったところからレンダしているようです。
また、ここでも継承項目は追加できていません。
こういうことはサポートしていないのか、もしかしたら何か書き方があるのかな。
いずれにせよ、ソースを読んでいないので、後で読んでみます。
メモ
- 例えばJSファイル等へのパスが相対パスだった場合、継承する側のvmファイルからのパスになるので、テンプレートに相対パスを書く場合は、継承するvmと同じ深さに配置しておく。かパスをblockにしておき上書きする。
- テンプレートは別にvmでなくてもいい。だからモックで作ったhtmlにextendsをいくつか埋め込んでおいておけば、それだけで継承できる。
- Velocity Layout Servletとの組み合わせを後で試す。
- こういうのをがっつりやるならMayaaの方がいいかもしれない。でもすでにVelocityを使っているなら試す価値はある。
- ユーザディレクティブの作成はしたことないので、このソースは後で読んで色々試す。
- ここまでの設定はいちいちするのめんどうなので、id:hisaboh氏の許可を取ってプロジェクトごとbitbucketで公開しようかとも考えたが、ライセンス的によくわからないのでやめた。