Block Rockin’ Codes

back with another one of those block rockin' codes

Velocityでテンプレートの継承

Djangoが好きな理由の一つに、テンプレートの柔軟性があります。
特に、継承ができるところ、これは慣れると継承のないテンプレートエンジンでは物足りなさを感じてしまいます。


今回はSAStruts+Velocityの組み合わせで、テンプレートの継承はできないか探したところ、少し古いですが以下の記事を見つけました。

Velocityでテンプレートを継承する方法 - Random Note(


継承の利点についてもここに書かれている通りです。
Velocity Layout Servletもありますが、Djangoになれると継承の方がわかりやすいんじゃないかな?
特に構造が重要なHTMLなんかは多重継承的なことができれば構築も楽になり、保守もしやすいんじゃないかと思います。


早速、上記の記事で公開されていたソースを使って継承を試してみました。色々試してみたいので備忘録。
今回はEclipse上でDoltengプロジェクトを作り、そこにVelocityを入れていきます。

プロジェクトの用意


EclipseDoltengを入れてある前提です。
まず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で公開しようかとも考えたが、ライセンス的によくわからないのでやめた。