テンプレート(Jade)
Expressフレームワークでのアプリケーション開発の基本に、ビューの使用がある。ビューとは、テンプレートコードを記述したファイルのこと。Expressは運用上分離すべき機能のコードを分離できるように設計されている。app.jsにはサーバスクリプトを記述し、それぞれのURLルートにリクエストを受けた場合の挙動はroutes/index.jsに記述し、クライアントへのアウトプットを生成するロジックはviewsディレクトリ内のコードに記述する。

テンプレート言語はロジックによる動的コンテンツ生成の基礎部分を提供し、テンプレートエンジンはそのロジックをHTMLに変換する。

ExpressのデフォルトテンプレートエンジンであるJadeを使ってデータを表示する。

(Expressでは、Jade以外のテンプレートエンジンを使用することもできる。Expressがサポートしているテンプレートエンジンはhttps://github.com/visionmedia/express/wikiにリストされている。テンプレートエンジン間の比較はhttp://paularmstrong.github.io/node-templates/(英語)で参照できる。)

mimoeappディレクトリにprofile.jsというファイルを用意する。

profile.js
module.exports = {
ryan: {
name: "Ryan Dahl",
irc: "ryah",
twitter: "ryah",
github: "ry",
location: "San Francisco, USA",
description: "Creator of node.js"
}, isaac: {
name: "Isaac Schlueter",
irc: "isaacs",
twitter: "izs",
github: "isaacs",
location: "San Francisco, USA",
description: "Author of npm, core contributor"
}, bert: {
name: "Bert Belder",
irc: "piscisaureus",
twitter: "piscisaureus",
github: "piscisaureus",
location: "Netherlands",
description: "Windows support, overall contributor"
}, tj: {
name: "TJ Holowaychuk",
irc: "tjholowaychuk",
twitter: "tjholowaychuk",
github: "visionmedia",
location: "Victoria, BC, Canada",
description: "Author of express, jade and other popular modules"
}, felix: {
name: "Felix Geisendorfer",
irc: "felixge",
twitter: "felixge",
github: "felixge",
location: "Berlin, Germany",
description: "Author of formidable, active core developer"
}
};

app.getなどでapp.jsに設定したルートが残っている場合は、トップディレクトリ(/)以外のすべてのルートを削除しておく。

環境設定内で、Jadeはすでにデフォルトテンプレートエンジンに設定されているため、app,jsに手を加える必要はない。

routes/index.jsでは、index以外へのすべてのルートを削除しておく。削除すると以下の状態になっているはず。

routes/index.js
exports.index = function(req, res){
  res.render('index', { title: 'Express' });
};

res.renderメソッドはviewsディレクトリに格納されているindex.jadeを呼び出し、profile.jsのオブジェクトデータのテンプレートとして使用する。

profiles変数にデータを読み込んで、そのオブジェクトをres.renderの2つ目のパラメータに渡すオブジェクトに含めておく。

routes/index.js
var profiles = require('../profiles.js');   //追加
exports.index = function(req, res){
  res.render('index', { title: 'Profiles',profiles: profiles });  //変更
};

indexページのタイトルをProfilesに変更した。

ここでviews/index.jadeを編集する。express(1)コマンドで生成されたindex.ladeには次のような記述が含まれている。

views/index.jade
  h1= title
  p Welcome to #{title}

ここにtable要素を加えて、profiles.jsの各メンバーの詳細情報を出力する。

views/index.jade
//以下を追加
//インデントに注意! table#profilesのインデントレベルはh1、pと同レベルにしなければならない(tabのインデントではエラーが出る)

  table#profiles
    tr
      th Name
      th Irc
      th Twitter
      th Github
      th Locatio
n
      th Description
        each profile, id in profiles
          tr(id=id)
            each val in profile
              td #{val}

記述を終えたら、アプリケーションを起動する。

ターミナル
$ node app.js

ブラウザでhttp://localhost:3000にアクセスすると、次のように表示される。



res.renderの最初のパラメータにファイル名(index)を渡すと、viewsディレクトリにある同名のテンプレートファイル(index.jade)を呼び出す。Expressは、以下に示すapp,jsの記述でviewsディレクトリ内にJadeファイルが格納されていることを認識しているため、.Jade拡張子は省略できる。

app.js
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');

res.renderの2つ目のパラメータにはオブジェクトを渡す。このコードでは、titleとprofilesという2つのプロパティを持っている。これらのプロパティは、Jadeテンプレートでローカル変数として利用できる。ローカル変数としてバッファされた値を等号(=)で呼び出せる。あるいはJadeのインターポレーションラッピング(#{})を使って、変数名を#{val}のようにラッピングする。

Jadeは冗長な部分を削ぎ落としたテンプレート言語。ブラケット(<>)を取り去ったマークアップタグや、インデントベースの文法とブロック拡張記法(インデントの代わりにコロンでネスト構造を表現する方法)を持つ。文法もできるだけ小さいサイズに抑えていて、例えばHTMLタグのidとclassは、それぞれハッシュ記号(#)とドット(.)で表す。

例えば、

  table#profiles
    th Name

というJadeコードは、次のHTMLを生成する。

<table id="profiles"><th>Name</th></table>

(Jadeの文法の詳細についてはhttps://github.com/visionmedia/jadeを参照する。)

Jadeはイテレーションもサポートしている。このコードでは、2つのeachイテレータを使ってprofilesオブジェクトから値を抽出している。

views/index.jade
        each profile, id in profiles
          tr(id=id)
            each val in profile
              td #{val}

最初のeach文はprofilesオブジェクトを走査して(in profiles)、それぞれのプロフィール情報を持ったオブジェクトをprofileオブジェクトに格納し、さらにそれらのオブジェクトのID(ryan、issac、bertなど)をid変数に格納している。

次のtr文は、それぞれのプロフィール情報を<tr>タグに設定している。<tr>タグのid属性には、<tr id="ID値">のようにprofileのID(id変数)の値を設定します。Jadeでタグのの属性を設定する場合は、タグ名に続く括弧内にキーと値を挿入する(tr(key=value,Key2=value))。id属性を設定するショートカットとしてハッシュ記号(#)を使用できるが、id属性値として変数を渡す場合は括弧内に指定する方法で記述を行う。

2番目のeach文では、trタグの行から一段インデントが入っている。JavaScriptと違い、Jadeはインデントもロジックの一部なので、インデントの有無がコードの動作に直接影響を及ぼす。このeach文では、最初のeach文で得たprofileオブジェクトの内容を走査し、それぞれの値をval変数に格納している。次の行のtd文の#{val}でそれぞれの値を出力する。



JadeにJavaScriptを挿入する
Jadeにもスクリプト(JavaScript)を埋め込むことができる。スクリプトを使ってヘッダをより簡単に出力してみる。index.jadeを次のように変更する。

views/index.jade
extends layout

block content
  h1= title
  p Welcome to #{title}

  - var headers = ['Name','Irc','Twitter','Github','Location','Description'];

  table#profiles
    tr
      each header in headers
        th= header
      each profile, id in profiles
        tr(id=id)
          each val in profile
            td #{val}

Jadeは、行頭にダッシュ(-)があると、その行にJavaScriptが記述されていると認識する。ここではheaders配列を宣言し、Jadeのeachイテレータでヘッダを出力している。等号(=)を使ってheader変数を評価している。

JavaScriptではなく、Jadeの記法で同様の記述を行うこともできる。上記のコードの「- var headers...」の行を以下のように変更する。

views/index.jade
  headers = ['Name','Irc','Twitter','Github','Location','Description'];

Jade、はこの記述を先ほどの埋め込みJavaScriptの形に内部変換し実行する。



//Stylusがうまく起動せず、エラーを起こすので、一旦中断!!

Nodeクックブック p.186

/*
CSSプロセッサを使う(Stylus)
テンプレートエンジンでHTMLを出力できたので、今度はCSSでスタイルを適用する。プレーンなCSSを記述することもできるが、ExpressはCSSプリプロセッサと連携することができる。

StylusはExpressと連携できるCSSメタ言語の1つ。StylusはExpressとの連携を念頭に置いて開発されており、Jadeテンプレートエンジンと似通った方針で開発されている。

Stylusにスポットライトを当て、先ほど作成したprofilesテーブルにStylusを使ってスタイルを当てる方法を学ぶ。

mimoeappのディレクトリを利用する。

最初にStylusをセットアップする。
今回は、ここまでで構築している既存のプロジェクトを使用するが、最初から始める場合はexpress(1)コマンドでStylusベースのプロジェクトを生成する。

ターミナル
$ express --css stylus ★アプリケーション名

このコマンドを実行すると、package.jsonのdependenciesやapp.jsの環境設定の項目にstylusが追加される。app.jsには以下のように追加される。

app.use(require('stylus').middleware(path.join(__dirname, 'public')));

今回は、mimoeappプロジェクトディレクトリにあるpackage.jsonを編集する。

package.json
{  "name": "mimoeapp",  "version": "0.0.1",  "private": true,  "scripts": {    "start": "node app.js"  },  "dependencies": {    "express": "3.4.8",    "jade": "*",    "stylus": "*"  }}
依存関係を加えたところで、以下のコマンドを実行して、依存モジュールをインストールする。

ターミナル
$ npm install

app.jsの環境設定に、次の文を追加しておく。

app.js
app.set('view engine', 'jade');
//次のミドルウェアを追加
app.use(require('stylus').middleware({ src:__dirname + '/views', dest:__dirname + '/public'}));app.use(express.favicon());app.use(express.logger('dev'));app.use(express.json());
srcとdestにそれぞれ違うディレクトリを設定した。

Stylusファイルを格納するviews/stylesheetsディレクトリを作成しておく。ExpressはこのディレクトリからStylusファイルを自動的に見つけ出して処理を行い、CSSを生成する。生成したCSSはpublicディレクトリにある同じ名前のディレクトリ(public/stylesheets)に保存される。

Stylusファイルの編集を始める。まず、public/stylesheets/style.cssファイルをviews/stylesheetsディレクトリにコピーして、style.stylと拡張子を変更しておく。ファイルに以下のCSSが記述されている。

views/stylesheets/style.styl
body {  padding: 50px;  font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;}
a {  color: #00B7FF;}

StylusはプレーンなCSSと完全互換なので、ファイルを編集する必要はない。しかしここでは、Stylus独自の記法を紹介しておく。現在のCSSの内容から波括弧({})やコロン(:)、セミコロン(;)を取り去って、Stylusのインデントベースフォーマットに変換する。

views/stylesheets/style.styl
body  padding 50px  font 14px "Lucida Grande", Helvetica, Arial, sans-serif
a  color #00B7FF
ここで、先ほど作成した#profileテーブルにスタイルを適用する。@extendディレクティブを使って、#profileテーブルとそれぞれのtdやthの上下左右にpaddingを与える。以下をstyle.stylに追記する。

views/stylesheets/style.styl
.pad  padding 0.5em#profiles  @extend .pad  th    @extend .pad  td    @extend .pad

ブラウザが新しいCSSプロパティをサポートする際、そのプロパティが標準として成熟していない場合にはベンダープレフィックス(ブラウザベンダー固有の接頭辞)を追加してサポートすることがある。例えば、角丸の半径を指定するborder-radiusプロパティは、Firefoxでは当初-moz-border-radiusとして実装され、WebKit系ブラウザでは-webkit-border-radiusとして実装された。

移り変わりの激しいCSSプロパティに追随してCSSを書き、メンテナンスを行うには大変な労力を必要とする。Stylusのmixin機能がこの労力を大幅に低減する。

views/stylesheets/style.styl
//以下を#profilesの前に追加
borderIt(rad = 0, size = 1px, type = solid, col = #000)  border size type col  if rad   -webkit-border-radius rad   -moz-border-radius rad   border-radius rad
このmixinを#profilesテーブル、th、tdの各要素に適用する。

views/stylesheets/style.styl
#profiles  borderIt 20px 2px  //追加  @extend .pad  th    @extend .pad  td    @extend .pad    borderIt(col: #000 + 80%)  //追加
app.jsを実行し、ブラウザからサーバにアクセスすると、public/stylesheets/style.cssが生成され、ブラウザには以下のようなページが出力される。



画像



このStylusはExpressのモジュールとして動作しているが、StylusはExpressを介することなく動作することができる。しかし、Expressのモジュールとして追加した場合、CSSの生成に便利なミドルウェアメソッドを使うことができる。

express(1)コマンドでStylusを含んだプロジェクトを生成すると、app.useでStylusを呼び出す際のオプションにはsrcプロパティだけが設定されている。Stylusは.styl拡張子を持ったファイルを取得し、CSSに変換して、その結果を格納した.cssファイルを同じディレクトリに配置する。destプロパティを設定すると、Stylusコードをsrcディレクトリから読み出し、destディレクトリにCSSを配置する。

srcディレクトリはviewsに、destディレクトリはpublicに設定している。しかし、styles.stylがviewsのサブディレクトリにあったとしても、Stylusはそれらのファイルを発見し、destディレクトリ配下に対応するサブディレクトリに配置する。

layout.jadeファイルには、(Webサイトのルートディレクトリを基準に)/stylesheets/style.cssへのlinkタグが記述されている。style.stylファイルをviews/stylesheetsに作成すると、そのStylusファイルをもとに生成されたCSSはpublic/stylesheetsに格納される。スタティックファイル配信のルートディレクトリにはpublicディレクトリが設定されているため、http://localhost:3000/stylesheets/style.cssへのHTTPリクエストには、public/stylesheets/style.cssファイルが返される。

また、今回ではStylusの機能をいくつか使用した。

@extendディレクティブは継承のコンセプトに基づいたディレクティブ。クラス(.padなど)を作成し、@expendディレクティブを使ってそのクラスのすべてのプロパティを他の要素に与える。例えば、ここでの@expendは以下のCSSを生成する。
*/