Home

日記
次世代のフォーム処理Edit

Ajaxを利用することで、Webアプリのフォーム回りの処理もいろいろできるようになるわけだけど、その処理が主機能に関連するものの場合は、そう簡単にJavaScript必須にするわけにもいかない。

だから今後のフォーム回りの設計については、基本的にJavaScriptなしでもフル機能動作するフォーム設計を行った上で、もしもJavaScriptが利用できる場合はより快適に動作する、といった方針で作っていくことを考えていた。

基本的にはonSubmitをフックして、JavaScriptで対応できる処理の場合はJavaScript側で処理を代行した上で、return false;するようなアプローチね。

で、実際にそういう高機能なフォームを書いてみようと試してみたところ、すぐに手が止まってしまった。具体的にどう書くという方針が全然練れていなかったらしい。

というか、JavaScriptなしで完全に動作するフォームの上に、JavaScriptを使うとより快適に動作するフォーム機能を載せようというアプローチ自体に無理があるのかな。もちろんそういうアプローチが可能な場合もあるけれども、そういうアプローチが可能なケースはかなりレアな気もしてきた。

素直に、JavaScriptなし版のフォームとJavaScript必須なフォームを、それぞれ別に作った方がいいのかもしれない。この辺を追求してみた人っているのかな?

Published At2006-03-03 00:00Updated At2006-03-03 00:00

日記
ちょっとましになったかなEdit

先週の金曜日から昨日あたりまでなかなかすごい感じで、ちゃんと朝晩薬を飲んでいても、クスリが切れかかる時間帯(朝起きてすぐと夕方過ぎくらい)にはちょっとキていたんですが、今日はずいぶん楽になった気がする。このくらいなら(クスリが効いている間は)マスクなしでも大丈夫そう。

Published At2006-03-07 00:00Updated At2006-03-07 00:00

日記
smarty_modifier_jsonEdit

今後JSONを使う機会は多くなるだろうけど、JSONライブラリのどれを使うべきか決め手に欠ける。あと、「サーバーとクライアントでテンプレートを共有したい場合」なんてことも考えつつ、

function smarty_modifier_json($var)
{
require_once 'HTML/AJAX.php';
$jsonSerializer =& new HTML_AJAX_Serializer_JSON();
$json = $jsonSerializer->serialize($var);
return $json;
}

のようなSmartyプラグインを用意して、

{if $mode == 'json"}
{$data|json}
{else}
{!-- $dataをHTMLとして展開するコード  --}
{/if}

なんて書くようにしておくと、JSONライブラリを差し替えるのも比較的楽かも。将来性を考えると、Zend Frameworkに入っているZend_Json_Encoderあたりが良さそうかなー。

あとsmarty_modifier_jsonの中で

header('content-type: text/javascript; charset=utf-8');

までやっちゃうと副作用が大きすぎるかな?

そういや

{if $mode == 'php'}
{$data|serialize}

とかも一応対応しておいた方がいいのかな? この前ちょっと話題になっていたよね。

Published At2006-03-07 00:00Updated At2006-03-07 00:00

日記
次世代のフォーム処理 その2Edit

Zend Frameworkのプレビューリリースに同梱されているZFormっていうHTML_QuickFormの後継になりそうなフォーム処理クラス(まだ_HIGHLY EXPERIMENAL_だそうだ)が、Ajax対応を試みている模様。もともとHTML_QuickFormはサーバーとクライアント(JavaScript)両対応のバリデーション機能を持っていたけど、さらにAjaxにも透過的に対応させようとしているっぽい。

あいにく同梱されているZFormは、まともに動く状態じゃない(関連ライブラリのディレクトリ構成が現状とは違っているし、requireしている関連ファイルも存在していなかったりする)けど、アプローチ自体は何となく読み取れた。

  • すべてのフォーム要素(form自身も)はZFormElementを継承する。
  • ZFormElementのbehaviorとして、ZFormAjaxBehaviorを追加することによって、その要素に対してAjaxによる拡張を行う
  • ZFormAjaxBehaviorでは、要素をレンダリングする際に、その要素専用のイベントハンドラー(JavaScript関数)を出力し、指定されたイベント(デフォルトではonclick)とイベントハンドラーを結びつける
  • イベントハンドラーが呼ばれると、ZFormAjaxBehaviorによって指定されたXmlHttpRequestリクエストを呼び出す
    • ZFormElementがフォーム自身だった場合は、フォームのすべての要素の値をパラメータとして使う
    • ZFormElementがフォーム要素の一つだった場合は、その要素の値をパラメータとして使う
  • XmlHttpRequestリクエストが正常終了したら、指定されたコールバック関数が呼ばれる

で、指定されたコールバック関数ってのは特に自動生成される対象ではなさそう(生成する処理を見つけられてないだけかもしれないけど)なんで、そこはJavaScriptレベルで独自に記述しておいてね、って感じなんだろう。

具体的な使い方の例がないんだけど、雰囲気的には、

<script type="text/javascript">
function myFormOnSubmit(req)
{
//XmlHttpRequestリクエストが成功したときの処理を書く
}
function myInputOnChange(req)
{
//XmlHttpRequestリクエストが成功したときの処理を書く
}
</script>
<?php
$form = ZFormFactory::loadElement('ZForm', 'MyForm'); //フォーム
$behavior = new ZFormAjaxBehavior(
$form,
'MyForm',
'/path/to/api', //XmlHTTPRequestの呼び出し先URL
'myFormOnSubmit', //callback
true, //非同期
'POST',
'submit' //submitイベントをフック
);
$form->addBehavior($behavior);
// 以下フォーム要素を追加
$element = ZFormFactory::loadElement('ZFormInputElement', 'MyInputElement');
$elementBehavior = new ZFormAjaxBehavior(
$element,
'MyInputElement',
'/path/to/api', //XmlHTTPRequestの呼び出し先URL
'myInputOnChange', //callback
true, //非同期
'POST',
'change' //changeイベントをフック
);
$element->addBehavior($elementBehavior);
$form->appendChild($element);
$form->render();
?>

みたいな感じになるのかなー。肝心のZFormElementBehaviorが同梱されていないんで、behaviorの登録の仕方がいまいちわからないけど、まあきっとこんな感じになるんだろう。ちなみにMyFormの方ではフォームのPOST処理自体をAjaxで代替するイメージで、MyInputElementの方は一文字ごとの文字チェックとか入力候補表示とかを行うイメージね。

確かにこのやり方だったら、フォームを構成するすべての要素のすべてのイベントに、独自のイベントハンドラーを用意できるし、JavaScriptレベルで、定型の呼び出し処理を自分で書かなくてすみ、コア機能を実装するためのイベントハンドラーの中身だけを書けばよくなる。方向性としては悪くはない。

ただ、ZAjaxとかいう独自のJavaScriptライブラリ(あまり完成度が高くない。というか、必要最低限しかまだ実装されていないっぽい)を使っているあたりは、大きなマイナスだ。この辺はもうprototype.jsあたりを採用して欲しいよなー。

あと、HTML_QuickFormもそうだったけど、こうやってコードでフォームを組み立てるやり方は、結構だるくて自由度が低かったりするんで、本格的にこれでいけるのかがちょっと微妙。HTML_QuickFormの場合はSmartyRendererとかを使えば、ある程度出力をいじれたけど、ZFormElementはrenderで直接echoしちゃってたりして、その辺の融通が利きにくそうだし。

「XmlHttpRequestリクエスト送信処理は自動生成し、送信処理後のコールバックだけを記述すればいい」という方針をパクった上で、prototype.jsと組み合わせたやり方を別途考えていった方がいいかなー。ZFormの今後に期待するという手もあるけど、方針が確定して安定していくまでまだまだ時間がかかりそうだしなー。

あと実際に使ってみないと分からないところだけど、XmlHttpRequestリクエストが成功した後のコールバックだけで、やりたいことは一通り表現できるんだろうか? なんとなく、XmlHttpRequestリクエストを送る前のコールバックも登録できるようにしておいた方が、良さそうな気がする。

function myFormOnSubmiting(params)
{
//XmlHttpRequestリクエスト送信前に実行する処理を記述する
}

とかやっておいて、この関数の戻り値によっては、XmlHttpRequestリクエストの送信自体をキャンセルしたりとか。まあこの辺は「あったほうがいい」とか言い出すと複雑になる一方なんで、切り捨てることのメリットの方が大きかったのかもしれないけど。

Published At2006-03-08 00:00Updated At2006-03-08 00:00

日記
Zend Frameworkにはもうちょっと気を遣って欲しかったEdit

なんかZend Frameworkの構成コードやサンプルなんかも、全然セキュリティには気を遣ってないっぽいなー。まだプレビューリリースってことを差し引いたとしても、根本的にセキュリティっつーか状況に応じたエスケープという概念を省略しまくりなのが、ちょっときつい。

どうせなら標準関数として、

function htmlprintf()
{
$args = func_get_args();
$format = array_shift($args);
foreach ($args as $index => $value) {
$args[$index] = htmlspecialchars($value);
}
array_unshift($args, $format);
return call_user_func_array('sprintf', $args);
}
function sqlprintf()
{
$args = func_get_args();
$format = array_shift($args);
foreach ($args as $index => $value) {
$args[$index] = addslashes($value);
}
array_unshift($args, $format);
return call_user_func_array('sprintf', $args);
}
$html = '<span>%s</span><span>%d</span>';
echo htmlprintf($html, '<>"&', '345.3') . "\n";
echo htmlprintf($html, '234', 'adsfdas') . "\n";
$sql = 'select * from test where string = \'%s\' and number = %d';
echo sqlprintf($sql, '\'foo', '345.3') . "\n";
echo sqlprintf($sql, '234', 'adsfdas') . "\n";
C:\php>cli\php test.php
<span>&lt;&gt;&quot;&amp;</span><span>345</span>
<span>234</span><span>0</span>
select * from test where string = '\'foo' and number = 345
select * from test where string = '234' and number = 0

みたいな関数を用意しておけば、ずいぶんましになるのかなー。sqlprintfの方はもっとちゃんと作る必要がある(DB関連関数の呼び出しをフックして、最後に呼び出したDB関数用のエスケープを実行するようにする、とか)けど、これでもないよりはずいぶんましって気がする。

Published At2006-03-08 00:00Updated At2006-03-08 00:00

日記
次世代のフォーム処理 その3Edit

ZFormが使っていたアプローチをHTML_QuickFormに適用できないか、試しに検討。HTML_QuickForm関連クラス自体を書き直すのではなく、外付けで拡張する方法で。

外付けで拡張するとなると、HTML_QuickForm_Elementたちをいちいち拡張するわけにはいかない*1から、できるのはせいぜいHTML_QuickForm本体を拡張したクラスを作って、従来の各関係クラスの機能やインターフェースとの互換性を確保したまま、Ajax対応の仕組みを追加するアプローチだろう。

HTML_QuickForm_Rendererを拡張しちゃえば、出力部分はかなり大幅にいじれるけど、Rendererは基底クラスは単なるインターフェース定義で、実装は各派生クラスでやっちゃってるから、一カ所直せば済むって話にはならなそうだ。手を出さない方が無難だろう*2

となると、HTML_QuickFormにAjax関連の設定を追加するためのメソッドを追加し、HTML_QuickForm::acceptあたりを互換性を持ったまま書き換えて、追加されたAjax関連の設定を見ながら、必要なJavaScriptコードを吐き出す感じかなー。その際には各elementがIDを持っていない場合は、IDを振る仕組みとかも必要そうだ。イベントハンドラー自体は直接element出力時に出力しなくても、IDさえ分かるなら後付けでEvent.observeできる。

というわけで、まとめてみる。

  • HTML_QuickFormを拡張したHTML_QuickForm_Exを作る。$form =& new HTML_QuickForm()の部分を$form =& new HTML_QuickForm_Ex()に変えるだけで、従来のHTML_QuickFormを使っていたコードは完全に動作するようにする。
  • HTML_QuickForm_Ex::addAjaxHandler(mixed $element, string $event, mixed $callback, array $ajaxOptions)を追加し、各element($element == __ALL__の場合はform自体)にAjax拡張用の情報を追加する。
    • コールバックの実行をXmlHttpRequestがonCompleteな場合のみ対応するのならば、このようなメソッドでいいけど、コールバックのパターンを増やしたいならば、インターフェースをもっと練る必要がある
    • HTML_QuickForm_Ex::removeAjaxHandler(mixed $element, string $event, mixed callback)も用意しておいた方がいいだろう
  • HTML_QuickForm_Ex::accept(&$renderer)で、HTML_QuickForm::accept相当の機能を実行した後に、必要なJavaScriptコードを出力する機能を追加する
    • Renderer_Defaultの場合は、$renderer->_hiddenHtmlにJavaScriptコードを追加すればいいだろう(JavaScriptの実行順序を考えると、$renderer->_htmlにコードを追加する必要があるかも。いや、$render->_hiddenHtmlにすべて出力した上で、onloadで初期化コードを呼ぶようにすれば問題ないかな?)。
    • toArrayなRendererの場合は、$renderer->_ary['javascript']にJavaScriptコードを追加すればいいかな。初期化周りの問題は上と同じ。
    • toObjectは使ったことないんで(Flexyは使ってない)よくわからないけど、多分toArrayな場合と同様に、$renderer->_obj->javascriptにJavaScriptコードを追加すればいいんだよね?
    • その他のRendererは使わないんでパス。JavaScriptコードさえ生成できていれば、出力を追加するのは簡単だろう。
  • その他懸案事項
    • Rendererに割り込むあたりは、外部オブジェクトのプライベート変数を外からばりばり書き換えるアプローチなんで、将来の互換性は厳しい。
    • javascript側のコードを書いて、考えていたような処理がJavaScriptでできるかどうか試してみないとな。まだ単純なprototype.jsのテストコードしか書いたことがない。
    • HTML_QuickFormが出力するJavaScriptバリデーションコードと、主に実行順序的にバッティングしないかどうか検証が必要。確かあれはformのonsubmitに直接イベントハンドラーを書いていたはずだけど、prototype.jsでEvent.observeした場合は、どういう実行順序になるんだろう? あるいは上書き?
    • そういやコールバックをどうやって記述するか全然考えてない。多分普段はSmartyと組み合わせて使うだろうから、Smartyテンプレート側に記述するのが無難? あるいはコールバック関数の内容自体もここで登録できるようにする方がいい?
    • elementにIDがない場合にIDを振る処理は、acceptの最初にやるべきか。自動生成したUNIQUE PREFIX+element名とかにIDを固定しちゃった方が扱いは楽なんだけどな。
    • どうせprototype.jsを使うんだったら、Ajax.Updaterとかも使いたくなりそうだけど、仕組みが複雑になりすぎるかな?

*1 HTML_QuickForm経由の自動生成ではなく、HTML_QuickForm_Elementを直接newする使い方も珍しくないから、Elementレベルで拡張してしまうと互換性的に厳しい

*2 と書いてからRenderer周辺を読み直してみたら、やっぱりRendererの各実装は(直接Rendererを書き換えないまでも)意識しないわけにいかないっぽい。toHtml、toArray、toObjectの3パターンを意識しておけばいいのかな?

Published At2006-03-09 00:00Updated At2006-03-09 00:00

日記
次世代のフォーム処理 その4Edit

いや、よく考えたらこの仕組みはフォームに限定する必要がまったくないな。基本的にDOM要素のIDとイベント名が分かれば、イベントハンドラーの登録はできるわけで、フォーム生成ライブラリと連携しなければならない必然性は非常に薄い。

対象のDOM要素がフォームまたはフォーム要素じゃない場合は、XmlHttpRequestで送るパラメータを何にするかが未確定だけど、逆に言うと、渡されたDOM要素の種類がフォームまたはフォーム要素ならば、送るパラメータを自動で特定できるわけだ。

となってくると、HTML_QuickFormを拡張するとか考えずに、独立したライブラリとしてHTML_AjaxHandlerとかを作った方がいいかもしれない。

class HTML_AjaxHandler
{
var $_handlers = array();
function addHandler($domId, $eventName, $callbacks, $ajaxOptions)
{}
function removeHandler($domId, $eventName, $callbacks)
{}
function toJavaScript()
{}
}

とかで十分いけるかな? この程度だったら、コールバックのタイミングを複数対応にしたりしても、全体としてはさほど複雑にならないだろうから、その辺までやっちゃえるか。

Published At2006-03-09 00:00Updated At2006-03-09 00:00

日記
HTML_AjaxHandlerのプロトタイプEdit

昨日言っていたHTML_AjaxHandlerのプロトタイプ版を作ってみた。

デモのソースは複数のデモをまとめて書いてあって読みにくいんで、2行目に表示されている平方根の計算に関連する部分だけ抜き出して、解説してみる。

デモの内容としては、テキストボックスに数字を入れると、その右側にリアルタイムで平方根の計算結果が表示される、というもの。平方根の計算はサーバーサイドで行い、XmlHttpRequestでデータをやりとりしている。Ajaxのデモなんで、平方根なんてJavaScriptで計算すればいいじゃん、ってツッコミはなしね。

HTMLとして必要な要素は、以下。

<script src="prototype.js" type="text/javascript"></script>
平方根: <input type="text" name="rootnum" id="rootnum" value="" /> = <span id="rootnum_result"></span>

ここでは特にイベントハンドラー等は定義していないし、prototype.jsを読み込んでいる以外は、JavaScriptコードは書いていない。

続いて、PHPコード部分は、以下。

require_once 'HTML/AjaxHandler.php';
$ajax =& new HTML_AjaxHandler();
$ajax->addHandler(
'rootnum',
'keyup',
array(
'url' => 'api.php',
'method' => 'get',
'parameters' => 'command=root',
'container' => 'rootnum_result',
)
);
echo $ajax->toJavaScript();

これでイベントハンドラーの定義を行っている。

HTML_AjaxHandler::addHandlerの引数は、

  • $elementId - 対象となる要素のID
  • $eventName - ハンドリングするイベント(prototype.jsの仕様に合わせる)
  • $ajaxOptions - その他オプションいろいろ

となっている。$ajaxOptionsに関しては、基本的にprototype.jsのAjax.Request、Ajax.Updater、Ajax.PeriodicalUpdaterのオプション(およびパラメータ)を受け付ける。それ以外にも、HTML_AjaxHandler独自のオプションをいくつか受け付ける。

上記の例だと、以下のような感じ。

  • url - XmlHttpRequestするURL。
  • method - リクエストメソッド。
  • parameters - 初期パラメータ。対象の要素がフォームやフォーム要素の場合は、その内容も自動的に追加される。
  • container - Ajax.Updaterで更新される要素のID。containerが指定されている場合、Ajax.Requestではなく、Ajax.Updaterが呼ばれることになる。containerに加えてfrequencyが指定されている場合は、Ajax.PeriodicalUpdaterが呼ばれることになる。

で、最後のecho $ajax->toJavaScript()で、実際のイベントハンドラー等のJavaScriptコードを自動生成して出力している。どういうJavaScriptコードかというと、以下のような感じ。

<script type="text/javascript">
<!--
function AjaxHandler_autoParameters(e, params)
{
if (params == undefined) {params = '';}
if ($(e).value != undefined) {
params += (params != '' ? '&' : '') + Form.Element.serialize($(e));
} else if ($(e).tagName.toLowerCase() == 'form') {
params += (params != '' ? '&' : '') + Form.serialize($(e));
}
return params;
}
function AjaxHandler_autocreated_rootnum_keyup(event)
{
var url = 'api.php';
var options = {
method: 'get',
parameters: 'command=root',
asynchronous: true,
container: 'rootnum_result',
};
options['parameters'] = AjaxHandler_autoParameters('rootnum', options['parameters']);
var ajaxRequest = new Ajax.Updater(
'rootnum_result',
url,
options
);
Event.stop(event);
}
function AjaxHandler_autocreated_441130d9062b1_onInit()
{
Event.observe('rootnum', 'keyup', AjaxHandler_autocreated_rootnum_keyup, false);
}
Event.observe(window, 'load', AjaxHandler_autocreated_441130d9062b1_onInit, false);
-->
</script>

その他のデモは、

  • load時にAjax.PeriodicalUpdaterに登録して、現在時刻をAjaxで取得&表示する。loadの処理が微妙(DOM要素単位でもloadイベントがあると勘違いして作っていて、あとでそんなイベントはないことに気づいて、あわててごまかした産物)。
  • 二つのテキストボックスに入力された数値を、フォームのsubmitをフックして、足し算した結果を表示する(フォーム投稿処理をAjaxで代替するイメージ)
  • 二つのテキストボックスに入力された数値を、入力欄の更新をフックして、足し算した結果を表示する(パラメータの付け替えとか、複数のイベントハンドラで同じコールバックを使ってみたりとか)

なんて感じ。

まだ全然細かいところまで練れていないけれども、ひとまずこんな感じのアプローチで、Ajax対応が楽になるかどうかいろいろ使って試してみよう。一応HTML_AjaxHandlerのライセンスはLGPLにしてあるけど、まだプロトタイプレベルなんで、コードは全面的に変更される可能性がある。

ちなみに

なんで初期化(イベントハンドラの登録)をwindow.onload経由でやっているかというと、JavaScriptコードをHTMLヘッダ部に出力した場合、イベントハンドリングする対象のDOM要素がまだ生成(HTMLとして出力)されていない可能性があるから。

あと、XmlHttpRequestリクエストを実行する前に呼ばれる独自のコールバックオプションとして、parametersCallback、urlCallback、optionsCallbackが使える。それぞれ、Ajax.*を呼び出す直前に、JavaScriptコードでparameters、url、optionsを書き換えたいときに使う。

本当はもう一個、XmlHttpRequestを行うかどうかを選択するためのコールバックも必要かと思っていたんだけど、基本的な処理の流れ自体を分岐するオプションに関しては、それ以外にもいろいろなパターンがありそうだから、もっといろいろ具体的なパターンを考えてから実装した方が良さそうに思えたんで、今のところつけていない。

Published At2006-03-10 00:00Updated At2006-03-10 00:00

日記
HTML_AjaxHandlerのサンプルその2Edit

一番基本となる、Ajax.Requestを使うパターンを作ってなかったんで、そっちも作ってみた。

デモの内容は、最小値と最大値を入力してフォームをsubmitする(Σボタンをクリック or ENTERキーを押す)と、最小値から最大値までを加算した式と結果を表示する。

HTMLは、

<form id="rangeform">
min: <input type="text" name="min" id="min" />
max: <input type="text" name="max" id="max" />
<input type="submit" value="Σ">
</form>
<span id="range"></span>

な感じで、HTML_AjaxHandlerでハンドラーを登録するPHPコードは、

require_once 'HTML/AjaxHandler.php';
$ajax =& new HTML_AjaxHandler();
$ajax->addHandler(
'rangeform',
'submit',
array(
'url' => 'api.php',
'method' => 'get',
'parameters' => 'command=range',
'onSuccess' => 'rangeSuccess',
)
);
echo $ajax->toJavaScript();

な感じになる。前のデモと違って、containerがセットされていないから、使われるAjax.*はAjax.Requestになる。で、onSuccessのコールバック関数はrangeSuccessね。

わざわざAjax.Requestを使う意味をだすために、サーバーサイド(api.php?command=range&min=[最小値]&max=[最大値])では、

$min = intval($_GET['min']);
$max = intval($_GET['max']);
$range = range($min, $max);
$sum = 0; foreach ($range as $num) {$sum += $num;}
echo '{expression: "' . implode(' + ', $range) . '", sum: ' . $sum . ' }';

なんて感じでJSON形式で結果を返している。で、Ajax.RequestのonSuccessコールバックに登録されたrangeSuccess関数では、

function rangeSuccess(res)
{
eval('var result = ' + res.responseText + ';');
var html = result['expression'] + ' = ' + result['sum'];
$('range').innerHTML = html;
}

なんて感じで、evalして計算式文字列と結果数値を取り出して、その内容から出力を組み立てている。

なんてのが、Ajax.Request+自前のコールバック関数を使った場合の記述パターンになる。

Published At2006-03-11 00:00Updated At2006-03-11 00:00

日記
AjaxHandlerにあった方がいい分岐パターンEdit

実際にいくつもアプリケーションを作ってみないと本当のところは分からない*1気がするけど、さすがにテストのためにそれなりの規模のアプリケーションを書く気にもなれないから、ひとまず思考実験してみよう。

まず思いつくのはフォームからの投稿をAjaxで代替処理する際に、JavaScriptバリデータと連係し、バリデーションの結果がfalseだったら、XmlHttpRequestが発動する前に処理を停止する、という流れ。これは非常によく使いそう。

で、ポイントとなるのはJavaScriptバリデーションコードをどうやって書くのか、というところ。

もしも自前でバリデーションコードを書くのならば、AjaxHandlerが生成するJavaScriptコードで、XmlHttpRequestの実行前にリクエストを実行するかどうかを判断するためのコールバックを用意し、そこにバリデーションコードを書けばいい。

なんにしろこういうパターンは絶対にあるだろうから、Ajax.*を生成する直前に呼ぶcheckValidCallbackとかを作って、その結果がfalseだったら処理を中断する、というオプションを作った方がいいだろう。

ただ、バリデーションコードがライブラリ化されている場合、自分でその実行タイミングをハンドリングできない可能性もある。たとえば、AjaxHandlerと同じように、Event.observeでバリデーション処理をハンドリングするようなバリデーションライブラリだったら、その実行タイミングはイベントハンドラーの実行順序に依存する。

また、HTML_QuickFormみたいにform.onsubmitに直接関数が記述されるようなパターンの場合、同じformのsubmitにEvent.observeしたら、どういう処理が行われるのか、という問題がある。

「まずform.onsubmitに直接記述された関数が呼ばれ、その戻り値がfalseでなかった場合にのみEvent.observeした関数が呼ばれる」なんて感じだといいのだが、「Event.observeしたら、form.onsubmitに記述されていた関数呼び出しが無効になる」とか、「Event.observeした関数とform.onsubmitに記述された関数の実行順序は不定」とかだと、話はややこしくなる。

まずはその辺の挙動について調査してみるか。

*1 Ajaxを実用レベルで活用したアプリケーションに関する経験が足りなすぎる

Published At2006-03-11 00:00Updated At2006-03-11 00:00