最近よく使っているテンプレートエンジンのejs、気に入っているんですが、
extendがつかえないというところがちょっと不満でした。
これをなんとかする方法を見つけたので、紹介します。
テンプレートのextend
まず、extendというのがどういうことか。
例えば別のテンプレートエンジン・jadeでは、
- ヘッダーとフッターは共通で
- 中身のコンテンツだけ書く
- ただし、jsライブラリもひとつ追加で入れたい
というとき、以下のように書けます。
extends ./layout
block scripts
script(src="/underscore.js")
block content
h1 きじたいとる!
p 本文
!!! 5
html
head
title サイトタイトル
block scripts
script(src="/jquery.js")
body
section#content
block content
つまり、content.jadeはlayout.jadeを継承(extend)して、
必要な部分だけ追記しているわけです。
これによって、content.jadeは必要な情報だけ書けばよいことになるので、
ファイルが短くなり、変な場所をイジる危険・ミスの可能性も減ります。
ejsの場合
このextendが、ejsだとできない。
決まった別のテンプレートをincludeすることはできるんですが、
提供されている機能としてはそれだけです。
↑と同じことをやろうとした場合、こんなかんじです。
<% include header %>
<script src="/underscore.js">
<h1>きじたいとる!</h1>
<p>本文</p>
<% include footer%>
<!DOCTYPE html>
<html>
<head>
<title>サイトタイトル</title>
<script src="/jquery.js"></script>
</head>
<body>
<section id="content">
</section>
</body>
</html>
headerとfooterを分ける必要があり、
また追加のscript読み込みを、headタグの中に入れるのが無理です。
ぬー。
サンプルを見たらなんかあった
何とかならないのかなーと思いつつ、公式のサンプルを見ていたら、
いいものがありました。
<h1>Users</h1>
<% function user(user) { %>
<li><strong><%= user.name %></strong> is a <%= user.age %> year old <%= user.species %>.</li>
<% } %>
<ul>
<% users.map(user) %>
</ul>
おーーー、functionで囲めば、再利用可能な部分テンプレートを、テンプレート内で定義できる!
これを応用すれば、extendに近いことできそう!
てなわけで、いろいろ試した結果、以下の形でうまく動きました。
<% function scripts (buf) { %>
<script src="/underscore.js">
<% }; function content (buf) { %>
<h1>きじたいとる!</h1>
<p>本文</p>
<% }; include layout %>
<!DOCTYPE html>
<html>
<head>
<title>サイトタイトル</title>
<script src="/jquery.js"></script>
<% scripts(buf) %>
</head>
<body>
<section id="content">
<% content(buf) %>
</section>
</body>
</html>
bufってなにさ
これを使うときのポイントがひとつ。
関数定義・呼び出しの部分で、bufっていう引数が入ってますね。
これは、ejsのテンプレートファイルのコンテキストみたいなものが入ってる変数です。
(ejsのコード読んで判明。そういう内部変数みたいやなつは、ほんとは_bufとかそれっぽい名前にしてほしいけど…)
includeを使ってテンプレートが別ファイルになったとき、
このコンテキストが変わってしまうようで。
そのままでは、content.ejsで定義した関数は、常にcontent.ejsの範囲に出力される、というわけです。
実際、このbufを渡さない形でテンプレートを実行すると、以下のような悲しい結果が返ってきます。
<script src="/underscore.js">
<h1>きじたいとる!</h1>
<p>本文</p>
<!DOCTYPE html>
<html>
<head>
<title>サイトタイトル</title>
<script src="/jquery.js"></script>
</head>
<body>
<section id="content">
</section>
</body>
</html>
これはejsのバグって言ってもいいような、仕様って言ってもいいような。。
まとめ
ejsでもextendできるってことがわかったので、
不満だいぶ減った。
ついでに、includeするときの基準パスをカスタマイズする方法も見つけたので
そちらもまたそのうち。