「Node.jsの基本は練習したからWebサイトやWebアプリの作成に進みたい」
「EJSが何をするためのものか分からずコピペしながら勉強してきた」
「HTMLと同じように見えるけどEJSだと上手く画面に表示できないことがある」
本日はそんな方に向けてNode.jsのテンプレートエンジンであるEJSの使い方を解説していきます。
Node.jsはWebアプリケーションの開発において非常に強力なツールとなっていますが、その中でもejs(Embedded JavaScript templates)は動的なHTMLページを簡単に生成するための優れたテンプレートエンジンの1つです。
この記事ではNode.js環境でejsをどのように使って、ウェブページの表示を行うかに焦点を当てます。send、sendFile、redirect、render、includeなどのejsを活用した便利な機能を紹介しながら、基本的な使い方から応用までを詳しく解説していきます。
ejsのインストールから実際に画面表示を行うまでの手順をステップバイステップで説明していきますので、この記事を読むことでNode.jsとejsを組み合わせた効果的なWeb開発の手法を身につけることができるでしょう。
Node.JSのプロジェクトにEJSを導入する方法
EJSはサードパーティ製のライブラリなのでインストール作業が最初に必要です。
npm i ejs
expressと一緒に使うものなのでexpressがまだの場合は同じくインストールしておきます。
npm i express
expressをインスタンス化するとsetメソッドというものを使うことができて、こちらのメソッドでビューエンジンをEJSに設定します。
ビューエンジンというNodeJSで画面の表示を担当するものはEJSと決まっているわけではないので、以下のようにしてEJSを使うことを宣言しないといけないです。
app.set('view engine', 'ejs');
続いて以下のように拡張子をejsにしたファイルを用意します。
-views
|- index.ejs
|- about.ejs
|- create.ejs
|-404.ejs
-server.js
拡張子はejsですが中身は通常のHTMLと同じような構成で大丈夫です。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<nav>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/about">About</a></li>
<li><a href="/blogs/create">New</a></li>
</ul>
</nav>
<h1>Home</h1>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<nav>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/about">About</a></li>
<li><a href="/blogs/create">New</a></li>
</ul>
</nav>
<h1>About</h1>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<nav>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/about">About</a></li>
<li><a href="/blogs/create">New</a></li>
</ul>
</nav>
<h1>Home</h1>
<form>
<label>
タイトル
<input type="text" class="title" />
</label>
<label>
本文
<textarea type="text" class="content"></textarea>
</label>
<button>作成</button>
</form>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<nav>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/about">About</a></li>
<li><a href="/blogs/create">New</a></li>
</ul>
</nav>
<h1>Not Found</h1>
</body>
</html>
作成したejsファイルはexpressのルーティングの設定で、renderメソッドの引数に指定することで該当のパスにアクセスすれば表示されます。
renderメソッドはviewsフォルダに格納されているejsファイルを探して、ドメインに該当するパスを生成するメソッドです。
引数にはファイル名だけを指定して「.ejs」の部分は記載しません。
const express = require('express');
const nodemon = require('nodemon');
const app = express();
app.set('view engine', 'ejs');
app.listen(3000);
app.get('/', (req, res) => {
res.render('index');
});
app.get('/about', (req, res) => {
res.render('about');
});
app.get('/blogs/create', (req, res) => {
res.render('create');
});
app.use((req, res) => {
res.status(404).render('404');
});
http://localhost:3000だとindex.ejsが表示されることになり表示は以下のようになっています。
EJSを使うことでテキストやHTMLタグ以外に、JavaScriptのコードも埋め込むことができます。
これによってコンテンツの表示を動的に変化させることができて、ブログサイトやWebアプリを運営することに繋がります。
JavaScriptのコードは画面に表示するものは「<%= %>」で囲み、for文やif文など画面に表示しないものは「<% %>」で囲むことで実行できます。
例えば定数testの値を文字列の「テスト」として表示するには以下のようにEJSファイルに書きます。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<nav>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/about">About</a></li>
<li><a href="/blogs/create">New</a></li>
</ul>
</nav>
<!--ここを追加-->
<% const test="テスト" %>
<%= test %>
<h1>Home</h1>
</body>
</html>
繰り返し処理で表示する方法の前に、server.jsでデータを簡易的に用意しておきましょう。
const express = require('express');
const nodemon = require('nodemon');
const app = express();
app.set('view engine', 'ejs');
app.listen(3000);
app.get('/', (req, res) => {
// ここを追加
const blogs = [
{ title: 'title1', content: 'content1' },
{ title: 'title2', content: 'content2' },
{ title: 'title3', content: 'content3' },
];
res.render('index', {
blogs: blogs,
});
});
app.get('/about', (req, res) => {
res.render('about');
});
app.get('/blogs/create', (req, res) => {
res.render('create');
});
app.use((req, res) => {
res.status(404).render('404');
});
変数や配列をejsファイルに渡すにはrenderメソッドの第二引数でオブジェクト形式にします。
上記コードだとblogsという配列をblogsというキー名で渡しています。
ejsファイル側ではblogsを使うことができるようになり、繰り返し処理で以下のように書いてみましょう。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<nav>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/about">About</a></li>
<li><a href="/blogs/create">New</a></li>
</ul>
</nav>
<% const test="テスト" %>
<%= test %>
<!--ここを追加-->
<% if(blogs.length > 0){ %>
<% blogs.forEach(blog => { %>
<h2><%= blog.title %></h2>
<p><%= blog.content %></p>
<% }) %>
<% } else { %>
<p>投稿はありません</p>
<% } %>
<h1>Home</h1>
</body>
</html>
先ほど説明したようにif文やfor文の部分は画面に表示するコードではないので「<% %>」で囲みました。
一方で画面に表示するblog.titleなどは「<%= %>」で囲みました。
最初はどちらがどっちのタグで囲むのかややこしく感じますが慣れてくるまで練習してみてください。
CSSについてはheadタグ内でstyleタグで囲むことで表現できます。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<!--ここを追加-->
<style>
.title {
color: red;
}
</style>
</head>
<body>
<nav>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/about">About</a></li>
<li><a href="/blogs/create">New</a></li>
</ul>
</nav>
<% const test="テスト" %>
<%= test %>
<% if(blogs.length > 0){ %>
<% blogs.forEach(blog => { %>
<h2><%= blog.title %></h2>
<p><%= blog.content %></p>
<% }) %>
<% } else { %>
<p>投稿はありません</p>
<% } %>
<h1>Home</h1>
</body>
</html>
このようにheadタグの中も通常のHTMLと同じような使用方法になっていて、ページのtitleタグには任意のサイト名やタイトルを埋め込むことも可能です。
先にserver.jsのrenderメソッドの第二引数でtitleタグに埋め込むテキストを用意しておきます。
const express = require('express');
const nodemon = require('nodemon');
const app = express();
app.set('view engine', 'ejs');
app.listen(3000);
app.get('/', (req, res) => {
// ここを追加
const blogs = [
{ title: 'title1', content: 'content1' },
{ title: 'title2', content: 'content2' },
{ title: 'title3', content: 'content3' },
];
res.render('index', {
blogs: blogs,
});
});
app.get('/about', (req, res) => {
res.render('about');
});
app.get('/blogs/create', (req, res) => {
res.render('create');
});
app.use((req, res) => {
// ここを追加
title: 'タイトル',
res.status(404).render('404');
});
続いてejsファイル側のheadタグに以下のようにテンプレートタグを埋め込みます。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!--ここを変更-->
<title><%= title %></title>
<style>
.title {
color: red;
}
</style>
</head>
<body>
<nav>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/about">About</a></li>
<li><a href="/blogs/create">New</a></li>
</ul>
</nav>
<% const test="テスト" %>
<%= test %>
<% if(blogs.length > 0){ %>
<% blogs.forEach(blog => { %>
<h2><%= blog.title %></h2>
<p><%= blog.content %></p>
<% }) %>
<% } else { %>
<p>投稿はありません</p>
<% } %>
<h1>Home</h1>
</body>
</html>
ここまで何点かejsファイル内でコードを書いてきたので少しコード量が多くなってきました。
ejsでは共通するコードをテンプレートファイルとして分けておき、必要な場所で呼び出す使い方もできます。
headタグは全てのページにあって共通の内容が書かれているので、それぞれのejsファイルから切り取って以下のようにpartialsというフォルダ内でhead.ejsという新しいファイルに保存しておきます。
-views
|-partials
| |-head.ejs
|
|- index.ejs
|- about.ejs
|- create.ejs
|-404.ejs
-server.js
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!--ここを変更-->
<title><%= title %></title>
<style>
.title {
color: red;
}
</style>
</head>
切り取ったhead.ejsはそれぞれのEJSファイル内で以下のようにして呼び出します。
<!DOCTYPE html>
<html lang="en">
<!-- ここを追加 -->
<%- include("./partials/head.ejs") %>
<body>
<nav>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/about">About</a></li>
<li><a href="/blogs/create">New</a></li>
</ul>
</nav>
<% const test="テスト" %>
<%= test %>
<% if(blogs.length > 0){ %>
<% blogs.forEach(blog => { %>
<h2><%= blog.title %></h2>
<p><%= blog.content %></p>
<% }) %>
<% } else { %>
<p>投稿はありません</p>
<% } %>
<h1>Home</h1>
</body>
</html>
includeというメソッドがあり、引数にパスを指定することで切り出している別ファイルを呼び出すことができます。
またこのようなHTMLタグを外部ファイルから取得して埋め込むとき、EJSファイル内では「<%- %>」という囲み方をします。
変数の埋め込みで使っていた「<%= %>」を使うとHTMLタグがそのまま文字列として表示されてしまうためです。
HTMLタグは画面に出すのではなくブラウザで文字列に変換される特殊な記号です。
そのような特殊な記号は「<%- %>」で囲むことがルールになっています。
Node.JSにミドルウェアを使用する
Node.JSにはミドルウェアと呼ばれる「サーバーでリクエストを受けて、レスポンスを返すまでの間のタイミングで実行するコード」が用意できます。
代表的なメソッドにuseというメソッドがあり、以下のように使用することができます。
const express = require('express');
const nodemon = require('nodemon');
const app = express();
app.set('view engine', 'ejs');
app.listen(3000);
app.get('/', (req, res) => {
const blogs = [
{ title: 'title1', content: 'content1' },
{ title: 'title2', content: 'content2' },
{ title: 'title3', content: 'content3' },
];
res.render('index', {
blogs: blogs,
});
});
// ここを追加
app.use((req, res, next) => {
console.log('オリジナルのmiddlewareを作成しました');
next();
});
app.get('/about', (req, res) => {
res.render('about');
});
app.get('/blogs/create', (req, res) => {
res.render('create');
});
app.use((req, res) => {
title: 'タイトル',
res.status(404).render('404');
});
パスが「/」にあるときと「/about」の時の間でuseメソッドを設定すると、「トップページからアバウトページに遷移するときに実行する」という意味になります。
またミドルウェアはnextメソッドで締める必要があり、これがないとトップページからアバウトページに遷移する途中で止まってしまいます。
expressはミドルウェアのコードがどの時点で完了されるかを知りませんので、ミドルウェアのコードを抜けて良いタイミングをnextメソッドで知らせる仕組みです。
また上記コードのようにミドルウェアはサードパーティライブラリとしても用意されていて、インストールしてミドルウェアのライブラリを使用しても良いです。
試しにMorganというライブラリのミドルウェアを使ってみましょう。
npm i morgan
const express = require('express');
const nodemon = require('nodemon');
// ここを追加
const morgan = require('morgan');
const app = express();
app.set('view engine', 'ejs');
app.listen(3000);
app.get('/', (req, res) => {
const blogs = [
{ title: 'title1', content: 'content1' },
{ title: 'title2', content: 'content2' },
{ title: 'title3', content: 'content3' },
];
res.render('index', {
blogs: blogs,
});
});
// ここを追加
app.use(morgan('dev'));
app.get('/about', (req, res) => {
res.render('about');
});
app.get('/blogs/create', (req, res) => {
res.render('create');
});
app.use((req, res) => {
title: 'タイトル',
res.status(404).render('404');
});
Morganは実行ログを表示してくれるミドルウェアで、リクエストとレスポンスの間で何が動いているかを確認することで人気のライブラリです。
useメソッドにmorgan(“dev”)と入れるだけでOKです。
ライブラリのミドルウェアはライブラリごとにuseメソッドに渡すものが違いますので公式ドキュメントを確認するようにしましょう。
ちなみにexpressもライブラリなのですがミドルウェアを持っています。
staticというミドルウェアが代表的で、以下のようにすることでCSSファイルや画像ファイルなど外部の静的ファイルを読み込めるようにできます。
const express = require('express');
const nodemon = require('nodemon');
const morgan = require('morgan');
const app = express();
app.set('view engine', 'ejs');
app.listen(3000);
app.get('/', (req, res) => {
const blogs = [
{ title: 'title1', content: 'content1' },
{ title: 'title2', content: 'content2' },
{ title: 'title3', content: 'content3' },
];
res.render('index', {
blogs: blogs,
});
});
app.use(morgan('dev'));
// ここを追加
app.use(express.static('public'));
app.get('/about', (req, res) => {
res.render('about');
});
app.get('/blogs/create', (req, res) => {
res.render('create');
});
app.use((req, res) => {
title: 'タイトル',
res.status(404).render('404');
});
staticメソッドの引数に外部ファイルが格納されているフォルダ名を指定します。
上記のコードだとpublicというフォルダにstyle.cssや画像データを保存できます。
前章でheadタグの中でstyleタグを使ってCSSを指定しましたがstyle.cssのように外部ファイルとしてCSSを読み込ませてよくなりました。
-public
|-style.css
-views
|-partials
| |-head.ejs
|
|- index.ejs
|- about.ejs
|- create.ejs
|-404.ejs
-server.js
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title><%= title %></title>
<!--ここにあったstyleタグを削除-->
</head>
h1{
color: red;
}