search
LoginSignup
84

More than 5 years have passed since last update.

ejsでextendっぽいことをする方法

最近よく使っているテンプレートエンジンのejs、気に入っているんですが、
extendがつかえないというところがちょっと不満でした。

これをなんとかする方法を見つけたので、紹介します。

テンプレートのextend

まず、extendというのがどういうことか。

例えば別のテンプレートエンジン・jadeでは、

  • ヘッダーとフッターは共通で
  • 中身のコンテンツだけ書く
  • ただし、jsライブラリもひとつ追加で入れたい

というとき、以下のように書けます。

content.jade
extends ./layout

block scripts
  script(src="/underscore.js")

block content
  h1 きじたいとる!
  p 本文
layout.jade
!!! 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することはできるんですが、
提供されている機能としてはそれだけです。

↑と同じことをやろうとした場合、こんなかんじです。

content.ejs
<% include header %>
<script src="/underscore.js">
<h1>きじたいとる!</h1>
<p>本文</p>
<% include footer%>
header.ejs
<!DOCTYPE html>
<html>
<head>
<title>サイトタイトル</title>
<script src="/jquery.js"></script>
</head>
<body>
<section id="content">
footer.ejs
</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に近いことできそう!

てなわけで、いろいろ試した結果、以下の形でうまく動きました。

content.ejs
<% function scripts (buf) { %>

<script src="/underscore.js">

<% }; function content (buf) { %>

<h1>きじたいとる!</h1>
<p>本文</p>

<% }; include layout %>
layout.ejs
<!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するときの基準パスをカスタマイズする方法も見つけたので
そちらもまたそのうち。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
84