JavaScriptでDjangoのテンプレート風なもの
JavaScriptで要素を動的に付け加えるときに,Djangoのテンプレートみたいに動いてくれるものを作ってみたら便利でした.
Before
今までは,appendとかを沢山つかって要素を生成してた.jQueryのtext関数によるescapeも重要だったし.でもそうすると完成したhtmlの要素の構造がよくわからないのでいちいちコメントでこのようにメモを書いていた.
function addComment(container, comment) { /* container : jQuery object comment : { id, owner : { id: ..., dname: (decoded) }, post_id : ..., text : (decoded)... } <div class="comment_box"> <div class="icon"> <img src="..." /> </a> <div class="comment_text">Hello, World</div> <span class="comment_owner">すずき</span> <span class="comment_id">12345</span> <span class="comment_delete_button">del</div> </div> */ var c = $('<div class="comment_box"></div>'); var icon_elem = $('<div class="icon"><img width="32" src="' + comment.owner.img + '" /></div>'); var comment_text_elem = $('<div class="comment_text"></div>'); var comment_id_elem = $('<span class="comment_id">' + comment.id + '</span>'); var comment_owner_elem = $('<span class="comment_owner"><a href="' + comment.owner.link + '"></a></span>'); comment_text_elem.text(comment.text); /* このへんを呼んでも最終的な構造がどのようなものか分かりにくいので上のようにコメントで構造をメモしていた */ comment_owner_elem.text(comment.owner.dname); c.append(icon_elem); c.append(comment_text_elem); c.append(comment_owner_elem); c.append(comment_id_elem); container.append(c); }
After
でも今はこんな感じ.JavaScriptはヒアドキュメントがないので複数行の文字列を用意する際には末尾にバックスラッシュが必要.
function addComment(container, comment) { /* container : jQuery object comment : { id, owner : { id: ..., dname: (decoded) }, post_id : ..., text : (decoded)... } */ var format = '\ <div class="comment_box">\ <div class="icon">\ <img src="{{owner.img}}" />\ </div>\ <div class="comment_text">{{text|escape}}</div>\ <span class="comment_owner">\ <a href="{{owner.link}}">{{owner.dname|escape}}</a>\ </span>\ <span class="comment_id">{{id}}</span>\ </div>' var c = $($S.sub(format, comment)); container.append(c); }
ミソである$Sはこんなかんじ.
function substitute2(format, obj) { /* {{key|option}} <= target key may have dots; e.g. 'owner.dname' if the object is { owner : { dname : 'suzuki<' } }, then {{owner.dname|escape}} becomes 'suzuki<' */ var pattern = new RegExp('{{([^\\|{|}]+)(\\|(\\w+))?}}', 'm') ret = format while ((m = ret.match(pattern)) != null) { var target = m[0]; var keys = m[1].split('.'); var option = m[3]; var kobj = obj; for (var i=0; i<keys.length; ++i) { kobj = kobj[keys[i]]; } var replacement = kobj; if (typeof replacement == 'undefined') { log('No such attribute for the object'); } if (typeof option != 'undefined') { switch(option) { case "escape": replacement = $('<div/>').text(replacement).html() break; default: console.log("no such option"); } } ret = ret.replace(target, replacement); } return ret; } $S = { "sub" : substitute2 }
escapeオプションにより<や>のような文字もエスケープされます.シンプルだけれどもかなり便利になりました.