【EJS使い方】簡易CMSのようなものをつくる

##EJSとは
Node.jsでコンパイルできるHTMLテンプレートエンジン。HTMLの共通部分をJavaScriptのような書き方で効率化することができる便利なもの。

##EJSで簡易CMSのようなものを作る
今回やりたいことはタイトルの通り。EJSを使って簡易CMSのようなものを作りたい。EJSではJavaScriptのfor文のように繰り返し処理を扱うことができるから、この仕組を用いて簡易CMSのようなものを作ります。

##作ろうと思ったきっかけ
今の会社でHTMLメールの制作をしているのですが、これが毎回ほぼ使いまわしで、中身の変更をコピペで何回もしないといけなかった。また、コピペでやってるからたまに変更忘れとかが起こっていた。
これらをどうにか時間短縮とミスを減らすことができないかと考えた結果、EJSを使えば簡易的なCMSのようなものが作れそうだとなり実際にやってみた。

##まずはEJSが動く環境を作る
EJSはそのままでは機能しません。タスクランナーを使ってHTMLに変換してあげる必要があります。
今回はgulpで環境構築をしました。
###ディレクトリ図
今回、用意する環境は最終的にこんな感じ。
とりあえずnode_module、package.json、package-lock.json以外を用意します。

```
├── node_module(npmでインストールする)
│   └── 大量に色々パッケージが入っている
├── package.json(npmでインストールする)
├── package-lock.json(npmでインストールする)
├── gulpfile.js
├── dev(ここが作業フォルダになる)
│    └──index.ejs
└── dist(ここにejsがコンパイルされてhtmlとして出力される)
```

以下、環境構築の説明ですがgulpを動かすにはNode.jsをインストールしてnpmコマンドを動くようにしておく必要があります。
Node.jsのインストールからnpmコマンドを使えるようにするまではics.mediaさんのこちらの記事などを参考に済ませておいてください。
https://ics.media/entry/3290/

記事の「3. package.jsonファイルの作成」まで進めたら以下の必要なパッケージをインストールしてgulpファイルを用意できたら環境構築が完成です。ここからはコピペしてもらったら大丈夫です！

###必要なパッケージ
- gulp（gulpを動かすのに必要な本体）
- gulp-ejs（EJSをコンパイルするもの）
- gulp-rename（EJSをコンパイルして.htmlの形式にリネームするときに使用）

この3つが最低限必要なパッケージです。
今回はこれに加えてブラウザで表示確認もおこないたかったので以下の3つのパッケージもインストールします。

- browser-sync（ローカルサーバーの立ち上げ）
- gulp-plumber（エラー時の強制終了を防止）
- gulp-notify（エラー発生時にデスクトップ通知する）

これらをそれぞれnpm installしていきます。
コマンドプロンプト上からcdコマンドで作業フォルダにて下記、コマンドでインストール。そのままコピペでOK！
`npm i -D gulp`
`npm i -D gulp-ejs`
`npm i -D gulp-rename`
`npm i -D browser-sync`
`npm i -D gulp-plumber`
`npm i -D gulp-notify`

package.jsonの中身が以下のような感じになっていたらOK！
つぎはgulpファイルを準備します。

```ruby:package.json
"devDependencies": {
    "browser-sync": "^2.26.7",
    "gulp": "^4.0.2",
    "gulp-ejs": "^5.1.0",
    "gulp-notify": "^3.2.0",
    "gulp-plumber": "^1.2.1",
    "gulp-rename": "^2.0.0"
  }
```

###gulpfile.jsを用意する
gulpfile.jsを用意します。gulpfile.jsのなかにどのような処理を実行するかということを書き込むことで動かすことができます。以下をgulpfile.jsにコピペしたら大丈夫です。

コードの詳しい説明は今回は省略。gulp4ではtask()での書き方は非推奨になっているようなのでタスクごとに関数でプライベートタスクを作成し、exportsでパブリックタスクにしています。今どきの書き方になっているかな？

これで完成。早速EJSを使っていきます。

```ruby:gulpfile.js
const { src, dest, watch, series } = require("gulp");
const browserSync = require("browser-sync").create();
const plumber = require("gulp-plumber");
const notify = require("gulp-notify");
const ejs = require("gulp-ejs");
const rename = require("gulp-rename");

function buildServer(done) {
  browserSync.init({
    server: {
      baseDir: "./dist/",
      port: 3000,
    },
    online: true,
  });
  done();
  console.log("server launched!");
}
function serverReload(done) {
  browserSync.reload();
  done();
  console.log("reload completed!");
}
function Ejs() {
  return src(["./dev/**/*.ejs", "!" + "./dev/**/_*.ejs"])
    .pipe(plumber({ errorHandler: notify.onError("Error: <%= error.message %>") }))
    .pipe(ejs({}, {}, { ext: ".html" }))
    .pipe(rename({ extname: ".html" }))
    .pipe(dest("./dist"));
}
function WatchFiles() {
  console.log("start watch!");
  watch("./dev/**/*.ejs", series(Ejs, serverReload));
  watch("./**/*.html", series(serverReload));
}
exports.default = series(buildServer, Ejs, WatchFiles);
exports.Ejs = Ejs;
```

##テンプレートをつくっていく
ここからEJSを使ってテンプレートを作成していきます。考えたいのはコンテンツ（テキストなど変更のある部分）とテンプレートの分離。これを実現するためにまずはindex.ejsの冒頭にオブジェクト形式でコンテンツの内容を用意する。

```ruby:index.ejs
    <%
        const main = {
            title:'EJSで簡易CMS！',
            lede: 'ここは繰り返しのないテンプレート',
        }
        let content = [
            {
            title:'繰り返しのコンテンツ',
            text: 'contentのオブジェクトの中に配列としてコンテンツを格納する。'
            },
            {
            title:'配列でコントロールしている',
            text: '配列を増やしていくとその分、コンテンツが繰り返していくテンプレート！'
            },
            {
            title:'コンテンツが増えても楽！',
            text: 'どんどんコンテンツが増えてもここだけ増やせばいいからミスが減る。'
            }
        ]
        let subcontent = [
            {
            title:'コンテンツがあるかないかの判断もできる！',
            text: 'コンテンツがあれば表示なかったら非表示みたいな出し分けもできます。配列の中身を消してコンパイルしてみてください。オブジェクトごと消してしまうとエラーになります。'
            }
        ]
    %>
```

そして上で用意したコンテンツ部分を表示させるテンプレート部分を用意する。

```ruby:index.ejs
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>EJSで簡易CMS！</title>
</head>
<body>
    <!-- 繰り返しのないコンテンツmainで定義したものがここに出力される -->
    <section>
        <h1><%- main.title %></h1>
        <p><%- main.lede %></p>
    </section>

    <!-- 繰り返しのコンテンツcontentで定義したものがここに出力される -->
    <% for (var i = 0; i < content.length; i++) { %>
    <section>
        <h1><%- content[i].title %></h1>
        <p><%- content[i].text %></p>
    </section>
    <% } %>

    <!-- コンテンツがあるかないかの判断をifで、コンテンツがあれば繰り返す処理をforでおこなっている -->
    <% if (subcontent === undefined) { %>

    <% } else { %>
        <% for (var i = 0; i < subcontent.length; i++) { %>
            <section>
                <h3><%- subcontent[i].title %></h1>
                <p><%- subcontent[i].text %></p>
            </section>
        <% } %>
    <% } %>
</body>
</html>
```

for文で繰り返し処理をおこなって同じコードを何回もコピペしなくていい仕組みになっています。つまり、上で用意したオブジェクト形式のコンテンツ部分を追加するだけでコンテンツを書き足していくことができます。また、if文で条件分岐をつくって表示するかしないかという処理もできます。

一度、テンプレートをつくってしまえばHTMLタグの閉じ忘れだったり、コピペ間違いだったりを減らすことができます。

そして、最終的にはHTMLに変換してあげる必要があるので作業フォルダで
`npx gulp`
もしくは
`npx gulp Ejs`
のコマンドを実行します。

npx gulpはEJSのコンパイルとブラウザプレビューができます。
npx gulp EjsではEJSのコンパイルだけしてくれます。

そうしてdistフォルダにhtmlファイルが出力されてこのようになります。

```ruby:index.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>EJSで簡易CMS！</title>
</head>
<body>
    <!-- 繰り返しのないコンテンツmainで定義したものがここに出力される -->
    <section>
        <h1>EJSで簡易CMS！</h1>
        <p>ここは繰り返しのないテンプレート</p>
    </section>

    <!-- 繰り返しのコンテンツcontentで定義したものがここに出力される -->
    <section>
        <h1>繰り返しのコンテンツ</h1>
        <p>contentのオブジェクトの中に配列としてコンテンツを格納する。</p>
    </section>

    <section>
        <h1>配列でコントロールしている</h1>
        <p>配列を増やしていくとその分、コンテンツが繰り返していくテンプレート！</p>
    </section>

    <section>
        <h1>コンテンツが増えても楽！</h1>
        <p>どんどんコンテンツが増えてもここだけ増やせばいいからミスが減る。</p>
    </section>

    <!-- コンテンツがあるかないかの判断をifで、コンテンツがあれば繰り返す処理をforでおこなっている -->
    <section>
        <h3>コンテンツがあるかないかの判断もできる！</h1>
        <p>コンテンツがあれば表示なかったら非表示みたいな出し分けもできます。配列の中身を消してコンパイルしてみてください。オブジェクトごと消してしまうとエラーになります。</p>
    </section>

</body>
</html>
```
##全体のソースコード
今回、作成したソースコードはgithubにあげているのでダウンロードもしくはクローンしてみてください。
https://github.com/makkii-y/EJS-template

##感想
つくった感想としては、一度、テンプレートをつくってしまえばHTMLタグの閉じ忘れだったり、コピペ間違いだったりを減らすことができるという点がいいと思いました。
CMSを使うほどではないけど簡単に管理したいといったときにも威力を発揮するのではと思います。

##参考にさせていただいた記事
https://ics.media/entry/3290/
https://www.i-ryo.com/entry/2020/05/17/094446
https://qiita.com/y_hokkey/items/31f1daa6cecb5f4ea4c9