Template Fragment

テンプレートフラグメント #

HTMLを生成する際にテンプレートエンジンが使われます。ブラウザが表示中の一部分のみをサーバーからとってきたデータで置き換えるようなことをする場合、テンプレートでその置き換える部分のみを生成できることが望ましくなります。

一部分のみを生成するテンプレートを個別に持つのではなく、テンプレートとしてはページ全体を持っていて、その全体テンプレートから一部分のみを出力することを考えます。この構成にしておけば個別部分のテンプレートをファイルとして持たなくてもよいため、アプリケーションの構造がシンプルになります。

ページ全体を含むテンプレート中の一部のみを出力させることを、ここではテンプレートフラグメントといっています。

Go #

Goの標準ライブラリにあるhtml/templateではテンプレートフラグメントを行うことが可能です。

まずテンプレートファイルでは、blockとしてフラグメントの部分を含みます。 下の内容のファイルをtamplates/page.htmlとして保存しておきます。

<html>
<body>
<p>テストテンプレート</p>
{{block "fragmentOnly" .}}<div>入力:{{ .value }}</div>{{end}}
</body>
</html>

テンプレート名によって全体のテンプレートか、フラグメントのみなのかを切り替えることができます。 全体の場合はファイル名のpage.html、フラグメントの場合はテンプレート内のblockとしておいたfragmentOnlyを指定します。

package main

import (
 "fmt"
 "html/template"
 "os"
)

func main() {
 page, err := template.ParseGlob("templates/*")
 if err != nil {
  panic(err)
 }
 data := make(map[string]string)
 data["value"] = "テンプレート全体"

 fmt.Println("\n◆テンプレート全体の出力◆")
 if err := page.ExecuteTemplate(os.Stdout, "page.html", data); err != nil {
  panic(err)
 }
 fmt.Println("◆ここまで◆")

 data["value"] = "フラグメントのみ"

 fmt.Println("\n◆フラグメントの出力◆")
 if err := page.ExecuteTemplate(os.Stdout, "fragmentOnly", data); err != nil {
  panic(err)
 }
 fmt.Println("◆ここまで◆")
}

実行結果は下のようになります。

◆テンプレート全体の出力◆
<html>
<body>
<p>テストテンプレート</p>
<div>入力:テンプレート全体</div>
</body>
</html>◆ここまで◆

◆フラグメントの出力◆
<div>入力:フラグメントのみ</div>◆ここまで◆

Python #

jinja2-fragmentsを使います。pipで次のコマンドでインストールします

pip install jinja2-fragments

テンプレートファイルは次のようになります。これをtamplates/page.htmlとして保存しておきます。

<html>
<body>
<p>テストテンプレート</p>
{% block fragment_only %}<div>入力:{{ value }}</div>{% endblock %}
</body>
</html>
from jinja2 import Environment, FileSystemLoader, select_autoescape
from jinja2_fragments import render_block

environment = Environment(
    loader=FileSystemLoader("templates"),
    autoescape=select_autoescape(("html",)),
)

template = environment.get_template("page.html")
rendered_html = template.render(value="テンプレート全体")
print("◆テンプレート全体の出力◆", rendered_html, "◆ここまで◆")

rendered_html = render_block(
    environment, "page.html", "fragment_only", value="フラグメントのみ"
)
print("◆フラグメントの出力◆", rendered_html, "◆ここまで◆")

出力結果は上のGoの場合と同じです。

Rust #

minijinjaを使います。テンプレートファイルは上のPythonの例でのpage.htmlと同じです。

Cargo.tomlには下のようにdependenciesを設定します。minijinjaのpath_loader関数を使うために、フィーチャーフラッグloaderをセットしています。

[dependencies]
minijinja = {version="2.1.0", features=["loader"]}
use minijinja::{context, path_loader, Environment};

fn main() {
    let mut environment = Environment::new();
    environment.set_loader(path_loader("templates"));
    let tmpl = environment.get_template("page.html").unwrap();

    let ctx = context!(value =>"テンプレート全体");
    let rendered_html = tmpl.render(ctx).unwrap();
    println!(
        "{}{}{}",
        "◆テンプレート全体の出力◆", rendered_html, "◆ここまで◆"
    );

    let ctx = context!(value => "フラグメントのみ");
    let mut state = tmpl.eval_to_state(ctx).unwrap();
    let rendered_html = state.render_block("fragment_only").unwrap();
    println!(
        "{}{}{}",
        "◆フラグメントの出力◆", rendered_html, "◆ここまで◆"
    );
}

Deno #

fraglatesを使います。fraglatesはNunjucksをベースにし、テンプレートフラグメントを実現しています。Nunjucksのテンプレート記法はJinja2とよく似ているため、テンプレートファイルは上のPythonの例でのpage.htmlと同じです。

import fraglates from "npm:fraglates";

const f = new fraglates({
    templates: "./templates",
});

const full_page = await f.render("page.html", {
    value: "テンプレート全体"
});

console.log("◆フラグメントの出力◆", full_page, "◆ここまで◆")

const fragment_only = await f.render("page.html#fragment_only", {
    value: "フラグメントのみ",
});

console.log("◆フラグメントの出力◆", fragment_only, "◆ここまで◆")