Play Frameworkのビューを共通化!テンプレート間のインクルード方法を徹底解説
生徒
「先生、Webサイトを作っていると、ヘッダーやフッターなど、どのページでも同じ内容を書く場所がありますよね。これって、ページを増やすたびに全部のファイルにコピーしないといけないんですか?」
先生
「いいえ、そんなことはありませんよ。Play FrameworkのビューエンジンであるTwirl(トワール)には、他のテンプレートファイルを読み込む『インクルード』という仕組みがあるんです。」
生徒
「インクルードを使えば、一箇所直すだけで全部のページが更新されるようになるんですか?」
先生
「その通りです!開発効率も上がるし、ミスも減らせます。それでは、具体的な使い方を見ていきましょう!」
1. テンプレートのインクルードとは?
Webアプリケーションを開発する際、サイト全体の共通デザイン(ヘッダー、ナビゲーションバー、フッターなど)を複数の画面で共有したい場面が必ず出てきます。 Play FrameworkのTwirlテンプレートでは、あるテンプレートの中から別のテンプレートを呼び出すことができます。これを「インクルード」や「テンプレートの再利用」と呼びます。
たとえば、header.scala.html という部品を一つ作っておけば、トップページでもお問い合わせページでも、一行書くだけでその部品を表示させることが可能です。
プログラミングの世界にはDRY(Don't Repeat Yourself)という「同じことを繰り返さない」という重要な原則がありますが、インクルードはこの原則を守るための強力な武器になります。
2. インクルードの基本操作
Twirlでのインクルードは、Javaのメソッドを呼び出すのと同じ感覚で行えます。
読み込みたいテンプレートファイルを @views.html.ファイル名() と記述するだけです。
たとえば、app/views/common/footer.scala.html というファイルがある場合、メインのページからは以下のように呼び出します。
<!DOCTYPE html>
<html>
<body>
<h1>メインコンテンツ</h1>
<p>ここにページの内容を書きます。</p>
@views.html.common.footer()
</body>
</html>
このように記述するだけで、コンパイル時に指定した場所の内容がガッチャンコと結合され、一つのHTMLとしてブラウザに表示されます。
3. 部品用テンプレートの作り方
読み込まれる側のテンプレート(部品)も、通常のテンプレートと作り方は同じです。
特別な設定は不要ですが、部品であることを分かりやすくするためにフォルダを分けたり(例:tagsやcommonフォルダ)、名前を工夫したりするのが一般的です。
<hr>
<footer>
<p>© 2026 Play Framework 学習ブログ. All Rights Reserved.</p>
</footer>
このように、部品側は <html> や <body> タグを含まない、断片的なHTMLとして作成します。
4. 引数を持ったテンプレートのインクルード
インクルードの素晴らしい点は、ただ静的なHTMLを表示するだけでなく、呼び出し元からデータ(変数)を渡せることです。 これにより、「見た目は同じだけど、表示される文字が違うボタン」のような部品を作ることができます。
部品側の先頭で @(title: String) のように受け取る引数を宣言しておけば、呼び出す側で @views.html.common.header("マイページ") のように値を渡せます。
@(label: String, color: String)
<button style="background-color: @color; color: white; padding: 10px;">
@label
</button>
5. コントローラからの呼び出しとデータの流れ
複数のテンプレートを組み合わせる場合でも、Javaのコントローラ側が意識するのは「一番親となるテンプレート」だけです。 コントローラから親テンプレートにデータを渡し、親から子(部品)へとデータをリレー形式で渡していくのがPlay Frameworkの標準的な流れです。
package controllers;
import play.mvc.*;
import views.html.*;
public class MyPageController extends Controller {
public Result index() {
String pageTitle = "ダッシュボード";
// 親テンプレート(index.scala.html)を呼び出す
return ok(views.html.index.render(pageTitle));
}
}
この pageTitle という変数は、まず index.scala.html に届き、そこからインクルードされたヘッダーなどの部品へと引き継がれていきます。
6. フォルダ階層がある場合の指定方法
プロジェクトが大きくなると、ビューファイルが増えて管理が大変になります。 その場合、フォルダ(パッケージ)を分けて整理しますが、インクルード時の記述もそれに対応させる必要があります。
フォルダ区切りは /(スラッシュ)ではなく .(ドット)で表現します。
もし app/views/admin/users/list.scala.html を呼び出すなら、@views.html.admin.users.list() となります。
これはJavaのパッケージ指定と同じルールなので、Javaに慣れている人なら直感的に理解できるはずです。
7. ループ内でのインクルード活用法
以前学習した @for 文とインクルードを組み合わせることで、リスト表示などを非常に綺麗に記述できます。
たとえば、商品リストを表示する際、商品一つの表示カードを別のテンプレートとして切り出しておくと便利です。
@(productList: List[models.Product])
<div class="row">
@for(product <- productList) {
<div class="col-md-4">
@views.html.components.productCard(product)
</div>
}
</div>
このように、複雑なHTML構造を部品化して @for 文の中をスッキリさせることで、コードの読みやすさが劇的に向上します。
8. インクルードを使うメリット:保守性の向上
インクルードを適切に使う最大のメリットは保守性(メンテナンスのしやすさ)です。 たとえば、サイトのロゴ画像を変更することになった場合、インクルードを使っていなければ全てのHTMLファイルを修正しなければなりません。
しかし、ヘッダーを部品化して共通化していれば、header.scala.html を一箇所書き換えるだけで、サイト全体のロゴが一瞬で切り替わります。
大規模なシステム開発になればなるほど、この「変更のしやすさ」が開発コストに大きく影響してくるのです。
9. インクルードとレイアウト機能の違い
Play Frameworkにはインクルードの他に「レイアウト」という機能もあります。 インクルードは「部品を呼び出す」のに対し、レイアウトは「全体の枠組みの中に中身を流し込む」という逆の発想です。
初心者のうちは、まずはインクルードを使って「共通する部品を切り出す」感覚を身につけるのがおすすめです。
慣れてきたら、サイト全体の枠を定義するレイアウト機能(main.scala.html など)と組み合わせて、より高度なテンプレート設計に挑戦してみましょう。
10. インクルード時の注意点:循環参照
最後に一つだけ注意点があります。それは循環参照(じゅんかんさんしょう)です。 テンプレートAがテンプレートBをインクルードし、さらにテンプレートBがテンプレートAをインクルードする、といった状態です。
これをやってしまうと、合わせ鏡のように無限に読み込みが繰り返され、エラーが発生してしまいます。 部品を作る際は、「どのテンプレートが親で、どのテンプレートが子なのか」という階層構造を意識して設計することが、トラブルを防ぐコツです。