テンプレートエンジンEJSを使ってみた

テンプレートエンジンのEJSを使ってみたので、Qiitaにまとめてみました。

公式サイトはこちら。（URLがとても短い！）
https://ejs.co/

# テンプレートエンジンとは

SPA（シングルページアプリケーション）でないWebアプリのバックエンドを開発している時に、よく直面する問題として、どうやってHTMLデータを生成するかがあります。

データベースから表示用のデータを引っ張ってきた後に、それをHTMLデータ上に埋め込んで、HTTPSのレスポンスにセットして、クライアントに投げる時ですね。

そのような問題の解決策として、テンプレートエンジンが、昔からよく使われてました。（ビューエンジンと呼ばれることもあります。）

Javaだと、[Apache Velocity](https://velocity.apache.org/)や、[FreeMarker](https://freemarker.apache.org/index.html)などがあります。
.NETだと、[Razor](https://learn.microsoft.com/ja-jp/aspnet/core/mvc/views/razor?view=aspnetcore-8.0)が有名です。

FreeMarkerの公式サイトにあった絵がわかりやすいので、ここに貼り付けますが、

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3680908/894659c6-9658-cce5-be1c-112dbb072eea.png)

左上にテンプレートファイルを定義して、左下のプログラム内で変数をセットしておき、
それらをマージすることで、右のデータを生成します。


# EJSの特徴

EJSは、「Embedded JavaScript templating」の略ですが、Eは、Effective, Elegant, Easyでもあるようです。

Javascriptベースで動くので、クライアントサイドで実行できますし、
Node.jsを使っていれば、サーバサイドでも実行することもできます。

# Hello, World

クライアントサイドで実行してみましょう。
事前に何かをインストールする必要はありません。

```html
<!DOCTYPE html>
<html>
<head>
  <script src="https://github.com/mde/ejs/releases/download/v3.1.9/ejs.min.js"></script>
  <script>
    window.onload = (() => {
      // 表示したいデータを含んだJSONオブジェクト。
      let data = {names: ['geddy', 'neil', 'alex']};
      // データを表示するテンプレート。namesをループで回して、中身を順番にliタグで出力する。
      let str = '<% names.forEach((name) => { %><li><%= name %></li><% }) %>';
      // テンプレートとデータを融合させて、HTMLデータを生成。
      let html = ejs.render(str, data);
      console.log('html : ' + html);
      // 大元のHTML内に、生成されたHTMLデータを埋め込む。
      document.getElementById('target').innerHTML = html;
    });
  </script>
</head>
<body>
  <ul id="target">target</ul>
</body>
</html>
```

ブラウザの表示結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3680908/2aac542f-475f-e159-8df8-49460fc8719b.png)

console.logの出力結果
```
html : <li>geddy</li><li>neil</li><li>alex</li>
```

この例だと、Javascriptのループ文とDOM操作を使えば、同じことができるので、あまりありがたみはありませんが:sweat_smile:、EJSを簡単に試すことができます。

# Node.js(Express)内で使う時の例

EJSのテンプレート内容を、拡張子.ejsのファイルに定義しておきます。
事前にejsのインストールが必要なので、公式サイトを見て行ってください。

以下はMVCパターンでフォルダを分けたときの例です。
```
src
├── controllers
│   ├── routes.ts
├── models
└── views
    ├── pages(画面ごとのejsファイルを定義します)
    │   └── hoge.ejs
    └── partials(複数の画面から、共通で使われるUI部品を定義します)
        └── header.ejs
```
　
/hogeが呼ばれたら、pagesの下においた、hoge.ejsを元に、HTMLデータを生成して、クライアントに返します。
```typescript:routes.ts
  // コードの一部のみ記載している。

  // Expressのビューエンジンでejsを使用する。
  app.set("view engine", "ejs");
  // テンプレートファイルを探しに行くときは、viewsフォルダを基準とする。
  app.set("views", path.join(__dirname, '../views'));

  app.get("/hoge", function (req, res, next) {
    // ビジネスロジック(models)のコードを利用して、データを取得または生成して、param1とparam2にセットする想定。
    const data = {'param1': '', 'param2': ''}; 
    res.render("pages/hoge.ejs", data);
  });
```

hoge.ejsの中では、パラメータとして受け取った、param1とparam2を、HTMLデータに埋め込みます。また、共通部品である、partials/header.ejsを呼び出して、埋め込んでいます。
```html:hoge.ejs
<!DOCTYPE html>
<html>
<head>
  <!-- 省略 -->
</head>
<body>
  <%# ejsファイルの中から、別のejsファイルを、パラメータ付きで呼び出す。%#から始まるこの行は、出力されないコメントを示す。 %>
  <%- include('../partials/header.ejs', {'title': 'hoge'}); %>
  
  <p>param1は、<%= param1 %></p>
  
  <%# 以下のように、出力する際に、javascriptの関数を使って、加工できる。 %>
  <p>param2は、<%= param2.substring(0, 4); %></p>
</body>
</html>
```
　
以下は共通のUI部品です。以下の内容が、上のincludeと書いてあるところに入ります。
titleには、hogeという文字列が入ります。
```html:header.ejs
<header>
  <h1>ここは<%= title %>のページです</h1>
  <nav>
    <ul>
      <li><a href="news.html">News</a></li>
      <li><a href="menu.html">Menu</a></li>
      <li><a href="contact.html">Contact</a></li>
    </ul>
  </nav>
</header>
```

# まとめ

EJSを使うことで、スムーズにデータ表示を行うことができました。
htmlにscriptタグを仕込むだけですぐに試せるのは良いですね。

EJS以外に、以下のテンプレートエンジンもあるので、プロジェクトに合ったものを選びましょう！
* [Handlebars](https://handlebarsjs.com/)（タグが<%= %>ではなく、{{ }}になる）
* [pug](https://pugjs.org/api/getting-started.html)（テンプレートをYAMLっぽく書ける）