2006-03-09 [長年日記]
_ 次世代のフォーム処理 その3
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とかも使いたくなりそうだけど、仕組みが複雑になりすぎるかな?
_ 次世代のフォーム処理 その4
いや、よく考えたらこの仕組みはフォームに限定する必要がまったくないな。基本的に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()
{}
}
とかで十分いけるかな? この程度だったら、コールバックのタイミングを複数対応にしたりしても、全体としてはさほど複雑にならないだろうから、その辺までやっちゃえるか。


