Ajax with prototype.js の実験と Web アプリの MVC に関する妄想
さて,昨日の予定通り,本日は prototype.js の Ajax クラスを調べてみる。参考にしたのはこちら。だいぶ流行から乗り遅れたおかげで,資料が充実した環境で効率的に学べるという恩恵に与れるとは,マイペースも悪く無いなとおもう(我ながらおめでたい)。
Class オブジェクトくらいだと,それほど「prototype.js スゲェッ!」とは思わなかったが,Ajax クラスは確かにイイ感じ。
まずは,たんじゅんに database のテーブルをそのまま表示してみる実験をしてみた。これだったら普通に CGI を使った方がいいかもしれないけど,それなりに意味がある。CGI が HTML ではなく,XML をよこすようにすることで,CGI は完全にモデル,すなわちアプリケーションロジックを提供することに特化できるようになるからだ。
- M ... CGI
- V ... static な HTML,CSS
- C ... JavaScript
みたいな感じ? V と C に技術の入り込む余地はあまりないから,スクリプトで公開してしまっても OK だと考えると,このデザインはわりとアリかも知れないなあ。あ,でも現実には JavaScript の互換性の問題がシビアだったりするのだろうか?あー,あるいは M の隠蔽を考えると C の一部をサーバーサイドに置いて,ある程度整形された V を出力するようにしないとだめとかかなぁ・・・ぶつぶつ。
閑話休題。
で,実験の続き。具体的には,データベースのテーブルを以下のような XML で出力(Web API のつもり)。
<table rows="4" cols="3"> <schema> <field name="id" type="long" maxLength="1"/> <field name="name" type="var_string" maxLength="14"/> <field name="parent" type="long" maxLength="1"/> </schema> <data> <record id="1" name="Recipe" parent="0"/> <record id="2" name="Curry" parent="1"/> <record id="3" name="Beef" parent="2"/> <record id="4" name="Pork" parent="2"/> </data> </table>
で,HTML 中に
<table id="plain_table" />
という空の table を埋め込んでおく。これに JavaScript の DOM API を使って要素(やイベント)を注入するという仕組み。ちょっと長いけど JavaScript のコードも貼付けておこう。
var PlainTable = Class.create(); PlainTable.prototype = { table : null, url : null, initialize : function(tableId, url) { this.table = $(tableId); this.url = url; }, update : function(onCompleteCallback) { var ajax = new Ajax.Request(this.url, { method : 'get', onComplete : onCompleteCallback }); } };var PlainTable = Class.create(); PlainTable.prototype = { url : null, callback : null, indicator: null, initialize : function(tableId, url, indicator) { this.url = url; this.callback = function(req) { plaintable_onComplete(req, $(tableId), indicator); }; this.indicator = indicator; }, update : function() { this.indicator.run(); var ajax = new Ajax.Request(this.url, { method : 'get', onComplete : this.callback }); } };function plaintable_onComplete(req, table)function plaintable_onComplete(req, table, indicator) { if (4 != req.readyState || 200 != req.status) { alert("ERROR:"); return; } var i, j; var schema = req.responseXML.getElementsByTagName("schema")[0]; var data = req.responseXML.getElementsByTagName("data")[0]; // create header var tf = null; var tr = document.createElement("tr"); var fields = schema.getElementsByTagName("field"); for (i = 0; i < fields.length; ++i) { tf = document.createElement("th"); tf.className = "field-name"; tf.appendChild(document.createTextNode(fields[i].getAttribute("name"))); tr.appendChild(tf); } table.appendChild(tr); // data var records = data.getElementsByTagName("record"); for (i = 0; i < records.length; ++i) { tr = document.createElement("tr"); for (j = 0; j < fields.length; ++j) { tf = document.createElement("td"); tf.appendChild(document.createTextNode(records[i].getAttribute(fields[j].getAttribute("name")))); if ("long" == fields[j].getAttribute("type")) tf.className = "num"; tr.appendChild(tf); } table.appendChild(tr); } indicator.stop(); //added }
で,HTML 読み込み時などに
var indicator; var plainTable; addEvent(window, "load", pageInit); function pageInit() { indicator = new ProgressIndicator("progress_indicator"); plainTable = new PlainTable("plain_test", "http://localhost/~xoinu/cgi-bin/test_plain.cgi", indicator);plainTable = new PlainTable("plain_test", "http://localhost/~xoinu/cgi-bin/test_plain.cgi"); indicator.run(); plainTable.update(function(req) { plaintable_onComplete(req, plainTable.table); indicator.stop(); });plainTable.update(); }
こんな感じで pageInit() が呼ばれるようにする。さっき作った ProgressIndicator も埋め込んでみた。JavaScript は,先週の土曜日に考察よれば,this の振る舞いが C++ などと異なるので,plainTable.table を明示的に渡さなければならない。だからコールバックの登録が何だかぎこちなくなってしまっている。この辺りはまだ理解が怪しいところ。indicator.run(),indicator.stop(),plainTable.update() の呼び出し方法,callback の登録方法はまだ再考の余地ありということで。
→ 改良済み。だいぶすっきりした。