AjaxHelper::editorあれこれ

昨日ハマったんで備忘録的に。Ajax.InPlaceEditorについて。

AjaxHelper::editorはほんとに地獄だぜー(AA略

CakePHP: AjaxHelper::editorでonFailureは出来ない?を参照していただくとわかるとおり、$options['onFailure']が投げれない。

正確には

$ajax->editor($id,$url,array('onFailure'=>"alert('hoge');"))

というとき、

new Ajax.InPlaceEditor('id','url',{ajaxOptions:{asynchronous:true, evalScripts:true, onFailure: function(){ alert('hoge'); }}}

のようにajaxOptions内にレンダリングされてしまうのが問題の根幹。

場当たり的な対応だが、これはAjaxHelperのajaxOptionsとeditorOptionsの値をインスタンス生成後に書き換える事で一応回避できる。
つまり、AjaxHelperを使おうとするviewの*.ctp内の最初の行あたりに

$ajax->editorOptions = am($ajax->editorOptions,array('onFailure'));
unset($ajax->ajaxOptions['onFailure'])

で、onFailure項がAjaxHelper::__optionsForAjax()から_buildCallbacks()にかけて解釈されないようにしてしまう。

逆に言うとこのプロパティの調整によりajaxOptions側にonFailureが投げれなくなるが、Ajax.Responders.registerで
対応するのが筋って気がしてるので気にしない事にする。

これでめでたくonFailureが投げられるのだが、_buildCallbacks()経由じゃないから、function(){}でラッピングしてくれないので自分でやらないとダメ。

$ajax->editor($id,$url,array('onFailure'=>"function(ipe,transrator){ alert('hoge'); }"))

こんな感じ。

これで動くと思ったら大間違い

ここでさらに問題。今度はAjax.InPlaceEditor側(Javascript側)の問題。
RoR本家でもいくつかチケットが出てたが、InPlaceEditorのonFailureがトリガされないというもの。
このBlogで紹介されているパッチscript.aculo.usの後に読み込んで置くことでこのバグは解消されます。ただし将来のバージョンでどーなるか不明なので自己責任で!

で、ぶっちゃけCakePHP側でバリデーションどうすんのよ?

上記のコードでいうところの$urlに対してPOSTで値がやってくるのですが、エラーが発生したらどうするか?
$url=array('controller'=>'hoges','action'=>'edit',$id)だとして

class HogesController extend App_Controller{
    function edit( $id ){
        $field = $this->params['form']から適当にパース;
        $data = $this->params['form']から適当にパース;
        $this->Hoge->id = $id;
        if(!$this->Hoge->saveField($field, $data, true){
            $this->redirect(null,500,true); // redirect()でexitしちゃう 
        }
    }
}

status 500を返すのがHTTP的な良し悪しはともかくとして、Ajax.InPlaceEditor側のonFailureは目出度く動作するはず。

Ajax.InPlaceEditor(正確にはAjax.Updater)はonFailureがトリガされたときはevalScripts:trueで投げててもevalしてくれないので

// views/hoge/index.ctp

// onFailure的なおまじない
$ajax->editorOptions = am($ajax->editorOptions,array('onFailure'));
unset($ajax->ajaxOptions['onFailure'])

// onFailureでもevalしちゃえ><
$ajax->editor($id,$url,array(
    'onFailure'=>"function(ipe,transrator){ transrator.responseText.evalScripts(); }"
));

で、バリデータ側は

class HogesController extend AppController{
    function editor( $id ){
        $field = $this->params['form']から適当にパース;
        $data = $this->params['form']から適当にパース;
        $this->Hoge->id = $id;
        if(!$this->Hoge->saveField($field, $data, true){
            loadHelper('javascript');
            $javascript = new JavascriptHelper();
            $this->redirect(null,500); //exitしない
            echo $javascript->codeBlock(sprintf("alert('%s');",$errorMessage)); //Javascirptはいちゃえ
            exit(); 
        }
        // views/hoge/editor.ctpに行く
    }
}

と、こんな感じで動きましたとさ。
動作環境 : script.aculo.us-1.8.1 + InPlaceEditorパッチ + CakePHP 1.2RC3

ついでにFormHelper::datetimeの$attirbutes['interval']バグ

https://trac.cakephp.org/changeset/7711 本家リポジトリのChangesetの通りだけど、$timeFormat = '24'の時
intervalが無視されちゃう件。パッチ当てる以外解決方法無し!