Category Archives: Web技術

JavaScript テンプレートエンジン #3 (Loop実装)

さらに続きです。

今日はループを実装してみました。

TemplateEngine = new function() {
var currentNode;
this.setCurrentNode = setCurrentNode;
this.writeText = function (value) { return new WriteText(value); };
this.writeHtml = function (value) { return new WriteHtml(value); };
this.setAttribute = function (name, value) { return new SetAttribute(name, value); };
this.loopWhile = function(condition) { return new LoopWhile(condition); }
function setCurrentNode(node) {
currentNode = node;
}
function WriteText(value) {
this.execute = execute;
function execute() {
currentNode.text(value);
}
}
function WriteHtml(value) {
this.value = value;
this.execute = execute;
function execute() {
currentNode.html(value);
}
}
function SetAttribute(name, value) {
this.execute = execute;
function execute() {
currentNode.attr(name, value);
}
}
function LoopWhile(condition) {
this.execute = execute;
function execute() {
while (condition()) {
var clone = currentNode.clone(true);
clone.insertAfter(currentNode);
}
}
}
}
TemplateEngineRunner = new function() {
this.run = run;
function run(controller) {
for (var key in controller) {
TemplateEngine.setCurrentNode($('#' + key));
controller[key].execute();
}
}
}
<!DOCTYPE html>
<html lang="ja">
<head>
   <meta charset="utf-8">
   <title>Template Engine Test</title>
   <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<span id="test"></span>
<span id="testHtml"></span>
<span id="attr">test</span>
<span id="loop">loop</span>
<script type="text/javascript" src="http://www.google.com/jsapi"></script>
<script type="text/javascript">google.load("jquery", "1.4.2");</script>
<script type="text/javascript" src="templateEngine.js"></script>
<script>
   with (TemplateEngine) {
       var i = 0;
       var controller = {
           test: writeText('hoge<b>aaa</b>'),
           testHtml: writeHtml('<i>aaa</i>'),
           attr: setAttribute('style', 'font-weight: bold;'),
           loop: loopWhile(function() {return i++ < 10;})
       }
   }
   
   TemplateEngineRunner.run(controller);
</script>
</body>
</html>

出力結果

hogeaaa aaa test looplooplooplooplooplooplooplooplooplooploop


ループしながらそれぞれの要素をどうにかしないといけないのと、このままでは同じidが複数できてしまってDOM的にNGなのが気になります。が、そろそろ色々できるようになってきたので、GitHubあたりで公開したほうがいいでしょうか?とすると、プロジェクト名とか決めなきゃいけないですね。募集します(笑)

ループのネスト実装したけど

これじゃいけてないよね?

   with (TemplateEngine) {
var i = 0;
var data = {};
var controller = {
test: writeText(function() {return 'hoge<b>aaa</b>'}),
testHtml: writeHtml(function() {return '<i>aaa</i>';}),
attr: setAttribute('style', function() {return 'font-weight: bold;'}),
loop: loopWhile(
function() {return i++ < 10;},
{
counter: writeText(function() {return i;})
}
),
'for': loopFor(
function() {i = 0},
function() {return i < 3},
function() {i++;},
{
counterFor: writeText(function() {return i;})
}
)
}
}

まあ、でも、Mayaaで言うところの、${}が、function() {}に相当するから、仕方ないのかなこれは。

追記(疑問)

ところで、クラスのprivateにvarを、publicにthisを使うような使い分けを上記のようにやってるんだけど、この書き方って、一般的?

続き→http://d.hatena.ne.jp/s-ishigami/20101119/1290151078

JavaScriptクライアントサイドテンプレートエンジン(続き)

一昨日の続きです。

その後修正を加えたバージョンを公開します。

やったこと:

  1. 外部JS化しました。
  2. dataをcontrollerにしました。
  3. HTMLタグの出力と、属性の変更も実装してみました。

TeplateEngine.js (仮のファイル名)

TemplateEngine = new function() {
this.currentNode;
this.writeText = function (value) { return new WriteText(value); };
this.writeHtml = function (value) { return new WriteHtml(value); };
this.setAttribute = function (name, value) { return new SetAttribute(name, value); };
function WriteText(value) {
this.execute = execute;
function execute() {
TemplateEngine.currentNode.text(value);
}
}
function WriteHtml(value) {
this.execute = execute;
function execute() {
TemplateEngine.currentNode.html(value);
}
}
function SetAttribute(name, value) {
this.execute = execute;
function execute() {
TemplateEngine.currentNode.attr(name, value);
}
}
}
TemplateEngineRunner = new function() {
this.run = run;
function run(controller) {
for (var key in controller) {
TemplateEngine.currentNode = $('#' + key);
controller[key].execute();
}
}
}

test.html

<!DOCTYPE html>
<html lang="ja">
<head>
   <meta charset="utf-8">
   <title>Template Engine Test</title>
   <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<span id="test"></span>
<span id="testHtml"></span>
<span id="attr">test</span>
<script type="text/javascript" src="http://www.google.com/jsapi"></script>
<script type="text/javascript">google.load("jquery", "1.4.2");</script>
<script type="text/javascript" src="templateEngine.js"></script>
<script>
   var controller = {
       test: TemplateEngine.writeText('hoge<b>aaa</b>'),
       testHtml: TemplateEngine.writeHtml('<i>aaa</i>'),
       attr: TemplateEngine.setAttribute('style', 'font-weight: bold;')
   }
   
   TemplateEngineRunner.run(controller);
</script>
</body>
</html>

試してみると、Google AJAX APIを使ったためか、JSをロードして置換を始めるまで待ち時間がちょっとありますね。dummyといった文字列を出すのはあまりよくなさそうなので、普通に空欄にした方が良いと思いました。本当は、この程度のことならjQueryもいらないんですよね。

続く

クライアントサイドHTMLテンプレートエンジンを作ろうとしてみる。

サーバーサイドテンプレートエンジンはMayaaが好きなんだけど、これからはクライアントサイドの時代ですね!でも、デザイナーさんは、JavaScriptアレルギーな方が多いので、デザイナーにJavaScriptを書かせるのも難しいし、かといって、UIプログラミングは大変なので、出来ればプログラマーの力を借りずにデザイナーさんの力でどんどん開発出来たほうが生産性が高いと思います。その方がフィードバックが早いですから。

そんなわけで、自分をモチベートするために、実験的に作ってみました。

<!DOCTYPE html>
<html lang="ja">
<head>
   <meta charset="utf-8">
   <title>TemplateSample</title>
   <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<span id="test">dummy</span>
<script src="jquery-1.4.2.min.js"></script>
<!-- テンプレートエンジン部分(外部JS化) -->
<script>
   TemplateEngine = new function() {
       this.currentNode;
       this.writeText = writeText;
       
       function writeText(value) {
           return new WriteText(value);
       }
       
       function WriteText(value) {
           this.value = value;
           this.run = function() {
               TemplateEngine.currentNode.text(value);
           }
       }
   }
   
   TemplateEngineRunner = new function() {
       this.run = run;
       function run(data) {
           for (var key in data) {
               TemplateEngine.currentNode = $('#' + key);
               data[key].run();
           }
       }
   }
</sctipt>
<!-- テンプレートデータ部分(UIプログラマかデザイナが書く) -->
<script>
   var data = {
       test: TemplateEngine.writeText('hoge')
   }
</script>
<!-- 実行(JSONPのコールバック?) -->
<script> 
   TemplateEngineRunner.run(data);
</script>
</body>
</html>

これを実行すると、クライアントサイドで、dataに書いてある、mayaaで言うところのprocessorみたいなものが解釈されて、画面のHTMLを置換します。ソースが多いですが、ほんとどがフレームワーク部分なので、外部JSにして、デザイナーさんか、UIプログラマーが書くべきなのは以下の部分だけになります。

var data = {
test: TemplateEngine.writeText('hoge')
}

この、dataは、JSONPかなにかで非同期で取得できると良いかもしれません。
あと、思った以上にデザイナーさんは、JavaScriptが苦手でXMLが好きなので、JSONではなくXMLの方が好まれるのかもしれません。その場合、XMLをサーバーサイドでJSONに変換してあげるのがパフォーマンス的にも良いかもしれません。

まだまだ実験とすら言えないよちよち歩きのコードですが、この構成で、forとか実装できれば、それなりに使えるのではないかと夢見てみたりしてみます。

追記

上記コードをみて、「エッジなエンジニア」な方々は「JavaScriptはオブジェクトを保持するべき!こんな書き方じゃ最初の出力しか賄えないよ」って言われるかもしれないと考えました。しかし、そうではなくてこう考えればいいのです。

「UIは全部dataに帰着する」

dataを返すJSONに、dataと一緒にオブジェクトを返し、dataのプロパティをそれぞれのオブジェクトとリンクすれば良いのではないでしょうか?dataオブジェクトの変更は、随時画面に反映されるようにすれば、HTMLは単なる枠になり、UIはdataオブジェクトで全て操作できる。

プログラマーは、その向こうでオブジェクトなりを操作して、dataを仲介してプログラミングを行えばいいのです。dataというより、controllerですね。

そのこともあって、WriteTextがオブジェクトだったり工夫したつもりです。とはいえ、JavaScriptで壮大なことをやると概して(主にIEで)重くなるので、どこかで落しどころをつけなければいけないでしょう。

続く