最近ではweb系の開発には欠かせなくなってきたテンプレートエンジン。
ログインユーザーの名前をヘッダに表示するみたいな、単純にデータを埋め込むだけのものならいいのですが、
サーバから吐き出したデータをjavascriptで使いたいとなると様相は異なってきます。
どうにかしようと色々もがいた、そんなお話です。
環境
実際に私が触ってた環境は
- OS:Windows7
- サーバ:Node.js
- フレームワーク:Express
- テンプレート:Jade
って感じでしたのでサンプルはこれに従いますが、
テンプレートエンジンが違ってようがサーバサイドがPHPだろうが似たような感じでいけるかと思います。
基本編 htmlに埋め込み
一応基本のおさらいをしておくとJadeで変数を使うときは=をつける。それだけです。
div
span= str
こうするとspanタグの中に変数strの中身が書き込まれる。
テンプレート内で変数を宣言するなら
- var str = "吾輩は猫である。名前はまだない。"
div
span= str
こんな感じでかの有名な一句が書き込まれます。
応用編 javascriptに埋め込み
ではJade内の変数をjavascriptに埋め込みんでみましょう。
変数名をそのまま書くとjavascriptの変数だと認識されて「そんな変数知らねえよ」と怒られるので、
変数展開して埋め込みましょう。
- var numJade = 1
script(type='text/javascript').
var numJs = '#{numJade}';
「あれ、numJadeはnumberなのになんでシングルコーテーションで囲ってるの?」という方のために説明すると、
この場合は多分大丈夫なのですが、numJadeの中身がnullになったりすると出力結果がvar numJs = ;
となってエラーになります。
それを避けるための手グセのようなものです。
JSONとかはどうするの?
さっきの例はただのnumberだったので大したことなかったのですが、これがJSONとかになると大変です。
先ほどの例に従って
- var jsonJade = {a: '砂糖', b: '猿'}
script(type='text/javascript').
var jsonJs = '#{jsonJade}';
こんなことしたら出力結果はvar jsonJs = '[object object]'
みたいなことになるかと思います。
console.logにJSONを放り込んだ時に見るあれです。
じゃあどうするのかというと
JSON文字列で展開するか、JSON文字列で渡してパースするかです。
- var jsonJade = {a: '砂糖', b: '猿'}
script(type='text/javascript').
var jsonJs = !{JSON.stringify(jsonJade)};
あるいは
- var jsonJade = JSON.stringify({a: '砂糖', b: '猿'})
script(type='text/javascript').
var jsonJs = JSON.parse(#{jsonJade});
誰もが感じるはずです。
「なんかキモい...」と
直接javascriptに埋め込むのはやめよう
そもそもの話としてjavascript内に直接埋め込むのは良いのでしょうか。
上の例で見ると、numberなのにエラーを避けたいがために文字列にしちゃってるし、
スルーしてたけどJSONの時はヌルチェックどうするの?コーテーションつけたら変なことになるんじゃない?
とか厄介な話が出てきます。
また、個人的にはビューの中にjavascriptのコードが居座り続けるのと、
javascriptのコード内にテンプレートエンジンのシンタックスが浸食することに静かな怒りを覚えます。
〇〇さんがあなたのこと××って言ってたよ
ふざけた見出しですが、要は直接伝えづらいなら誰かに間に立ってもらおうということです。
完全に余談ですが、私は知人女性の胸元から下着が見えてたのでこっそり注意したら、後日別の知人に「直接言うとか馬鹿なの?その場にいる別の女性に言ってもらえよ」とたしなめられました。
それはさておき、どうもJadeの変数を直接javascriptに埋め込むのはどうにも角が立つので、
間に立ってもらう方が必要です。
そういうのにうってつけなのがhtmlのdata-*属性です。
何がいいって、jQueryで簡単に取り出せることです。
- var numJade = 1
- var jsonJade = {a: '砂糖', b: '猿'}
script(type='text/javascript',src="sample7.js" ,id="main", data-num=numJade, data-json=JSON.stringify(jsonJade))
(function(){
var $main = $("#main");
var numJs = $main.data('num');
var jsonJs = $main.data('json');
}).call(this);
$('#hoge').data('fuga')
でdata-fuga属性の値を取り出せます。データの型も自動で整えてくれます。
注意点としては、属性名はdata-fugaですがメソッドで指定するのはfugaということです。
また、属性名がdata-で始まってないと使えません。
$('#hoge').attr('data-fuga')
だと返り値は文字列になります。
棲みわけって大事 グレーゾーンも大事
こうすると何がいいかっていうと、Jadeとjavascriptの棲みわけがはっきりすることです。
jadeの仕事はhtmlを組み立てることなのに、javascriptの世界に足を踏み入れたから上記のなんかキモいコードになったわけです。
しかし、これならJadeはjavascriptには一切手をつけていません。
おかげでJadeとjavascriptを別ファイルに分割することもできました。
data-*属性というグレーゾーンのおかげで棲みわけがはっきりするというのもなんだかおかしな話な気もしますが。
まとめ
- テンプレートエンジンで変数をjavascriptに渡すのはやめよう
- data-*属性に埋め込んでjQuery.dataで取り出すと楽
[参考]
Jade公式
Jadeの記法について(あまりまとまっていない)
Jade template, how to pass concrete object to pages?