YUI2.8.1で日本語をAutoCompleteする
YUIも2.8.1。以前に書いたログと同じことを日本語で実装したくて、XHRDataSourceを使って書いたのだがうまくいかない。前にログを書いていたときのYUIのバージョンは2.6だった。
仕様は簡単で、以下の初期画面で「カタカナ」で書かれた商品名を入力する。
すると、以下のように(AutoCompleteだから当たり前だけど)合致する商品名が表示される。
この裏では商品コードをもっていて、商品名を選択してsubmitボタンを押して商品コードが取得できていることを確認する。ページのエンコードはUTF-8。
以前のサンプル(YUI本家のサンプルも同じ)では、ローカル(=HttpServer)下にあるJSファイル(UTF-8)を読み込んでAutoCompleteを実装している。このやり方で、日本語を試したらうまくいく。
ところが、DBからPHPでデータを取得(UTF-8)して、PHPのjson_encode関数を使って、以下のレスポンスを吐き出し、XHRDataSourceでハンドルしようとしてもうまくいかない。
[{"name":"\u30aa\u30aa\u30d9\u30f3\u30b1\u30a4\u30bd\u30a6","key":"A0007"},{"name":"\u30aa\u30b7\u30ed\u30a4\u30d0\u30ca","key":"A0013"},{"name":"\u30aa\u30b7\u30ed\u30a4\u30d0\u30ca\uff08\u30d4\u30f3\u30af\uff09","key":"A0009"},{"name":"\u30aa\u30df\u30ca\u30a8\u30b7","key":"A0009"},{"name":"\u30ab\u30ce\u30b3\u30e6\u30ea","key":"A0003"},{"name":"\u30af\u30ea\u30b9\u30de\u30b9\u30ed\u30fc\u30ba\u30fb\u30aa\u30ea\u30a8\u30f3\u30bf\u30ea\u30b9","key":"A0001"},{"name":"\u30bf\u30de\u30a2\u30b8\u30b5\u30a4","key":"A0008"},{"name":"\u30ce\u30ab\u30f3\u30be\u30a6","key":"A0006"},{"name":"\u30d2\u30aa\u30a6\u30ae","key":"A0002"},{"name":"\u30dd\u30fc\u30c1\u30e5\u30e9\u30ab","key":"A0014"},{"name":"\u30de\u30ea\u30e2\uff08\u5927\uff09","key":"A0004"},{"name":"\u30df\u30bd\u30cf\u30ae","key":"A0005"},{"name":"\u30e4\u30de\u30d6\u30ad\uff08\u82d7\uff09","key":"A00000002"}]
日本語はエスケープされてるが、これで問題ないはず。だが、インプットフィールドに入力をしても、先頭から商品名が表示されてしまう。FireBugで確認しても、XHRでちょこちょこリクエストが出ているが、取得されているのは、上で示した文字列。
うまくいかなかったコードは以下。
<!-- /** * testAutoComplete2.html * * (C) 2010, tetsuya.odaka(EzoGP). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ --> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <HTML> <HEAD> <TITLE>AutoComplete テスト</TITLE> <meta http-equiv="content-type" content="text/html; charset=utf-8"> <link rel="stylesheet" type="text/css" href="MyznalaCommon/scripts/yui/build/fonts/fonts-min.css" /> <link rel="stylesheet" type="text/css" href="MyznalaCommon/scripts/yui/build/autocomplete/assets/skins/sam/autocomplete.css" /> <style type="text/css"> #main { margin: 2px; padding: 3px; } #myAutoComplete { width:15em; /* set width here or else widget will expand to fit its container */ padding-bottom:2em; } #mySubmit { position:absolute; left:20em; margin-left:1em; /* place the button next to the input */ } </style> <script type="text/javascript" src="MyznalaCommon/scripts/yui/build/yahoo-dom-event/yahoo-dom-event.js"></script> <script type="text/javascript" src="MyznalaCommon/scripts/yui/build/animation/animation-min.js" ></script> <script type="text/javascript" src="MyznalaCommon/scripts/yui/build/datasource/datasource-min.js" ></script> <script type="text/javascript" src="MyznalaCommon/scripts/yui/build/autocomplete/autocomplete-min.js"></script> <script type="text/javascript" src="MyznalaCommon/scripts/yui/build/connection/connection-min.js" ></script> <script type="text/javascript" src="MyznalaCommon/scripts/yui/assets/data.js"></script> <script type="text/javascript"> var MyAC = function() { /* * コンストラクタ */ var Dom = YAHOO.util.Dom; var Event = YAHOO.util.Event; /***************************************************** * 初期処理 ******************************************************/ this.init = function( _config ) { var submitURL = _config.submitURL; var myInput = _config.myInput; var myContainer = _config.myContainer; var myHidden = _config.myHidden; var myForm = _config.myForm; // DataSourceインスタンスの生成 var oDS = new YAHOO.util.XHRDataSource(submitURL); oDS.responseType = YAHOO.util.DataSource.TYPE_JSARRAY; oDS.responseSchema = { fields : ['name','key'] }; // AutoCompleteインスタンスの生成 var oAC = new YAHOO.widget.AutoComplete(myInput, myContainer, oDS); // そのまま読み込む oAC.resultTypeList = false; // 隠しフィールドの取得。 var myHiddenField = YAHOO.util.Dom.get(myHidden); // AutoCompleteからitemを選んだときのハンドラ var myHandler = function(sType, aArgs) { // oArgeslength=3の配列。[0]はoAC、[1]は選ばれたHTMLLIelement、[2]は選ばれたobject literal var myAC = aArgs[0]; var elLI = aArgs[1]; var oData = aArgs[2]; // hiddenのinputタグに仕込む myHiddenField.value = oData.key; }; // AutoCompleteからitemを選んだイベントに、ハンドラを仕込む oAC.itemSelectEvent.subscribe(myHandler); // submitボタンが押されたときのハンドラ var onFormSubmit = function(e, myForm) { YAHOO.util.Event.preventDefault(e); alert("商品コード: " + myHiddenField.value); }; // Formのsubmitイベントに上のハンドラを仕込む。 YAHOO.util.Event.addListener(YAHOO.util.Dom.get(myForm), "submit", onFormSubmit); return { oDS: oDS, oAC: oAC }; }; return this; }; YAHOO.util.Event.onDOMReady( function() { var aObj = new MyAC(); var config = { submitURL : "catalogDaoGetKeyValueJSON.php", myInput : "myInput", myContainer : "myContainer", myHidden : "myHidden", myForm : "myForm" }; aObj.init(config); }, this, true ); </script> </HEAD> <body class=" yui-skin-sam"> <div id="main"> <p>AutoCompleteのサンプルです。<br/> AutoCompleteで選んだ項目に、関係する項目がSubmitされます。</p> <br> <form id="myForm" action="#"> <div id="myAutoComplete"> <input id="myInput" type="text"> <input id="mySubmit" type="submit" value="Submit"> <div id="myContainer"></div> </div> <input id="myHidden" type="hidden"> </form> </div> </body> </html>
結局、ConnectionManagerをつかって、イニシャルで商品名と商品コードを読みこんじゃうことにした(以下のコード)が、これだとデータが膨れてきたときに問題がありそう。XHRDataSourceをつかう場合、ちょこちょこリクエストがでるので、アクセスが増えた場合を考えると、プロセス数の上限に引っかかって逆に遅くなるかもしれないし、微妙なところ。
XHRDataSourceで入力値をひろって、リクエストパラメータでサーバーに渡し、SQLで(LimitとFetch Sizeを)ハンドルして「頭出し」というのもありかなぁ(仕様的には殆ど変わらないし)。
<!-- /** * testAutoComplete.html * * (C) 2010, tetsuya.odaka(EzoGP). * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ --> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <HTML> <HEAD> <TITLE>AutoComplete テスト</TITLE> <meta http-equiv="content-type" content="text/html; charset=utf-8"> <link rel="stylesheet" type="text/css" href="MyznalaCommon/scripts/yui/build/fonts/fonts-min.css" /> <link rel="stylesheet" type="text/css" href="MyznalaCommon/scripts/yui/build/autocomplete/assets/skins/sam/autocomplete.css" /> <style type="text/css"> #main { margin: 2px; padding: 3px; } #myAutoComplete { width:15em; /* set width here or else widget will expand to fit its container */ padding-bottom:2em; } #mySubmit { position:absolute; left:20em; margin-left:1em; /* place the button next to the input */ } </style> <script type="text/javascript" src="MyznalaCommon/scripts/yui/build/yahoo-dom-event/yahoo-dom-event.js"></script> <script type="text/javascript" src="MyznalaCommon/scripts/yui/build/animation/animation-min.js" ></script> <script type="text/javascript" src="MyznalaCommon/scripts/yui/build/datasource/datasource-min.js" ></script> <script type="text/javascript" src="MyznalaCommon/scripts/yui/build/autocomplete/autocomplete-min.js"></script> <script type="text/javascript" src="MyznalaCommon/scripts/yui/build/connection/connection-min.js" ></script> <script type="text/javascript" src="MyznalaCommon/scripts/yui/assets/data.js"></script> <script type="text/javascript"> /** * XHRでAutoCompleteするオブジェクト * */ var MyAC = function() { /* * コンストラクタ */ var Dom = YAHOO.util.Dom; var Event = YAHOO.util.Event; var Connect = YAHOO.util.Connect; /***************************************************** * サブミットする *****************************************************/ var submit = function( _config ) { var submitURL = _config.submitURL; ajaxCallback.argument = _config; // データの取得。 Connect.asyncRequest('GET', submitURL, ajaxCallback); } /***************************************************** * Ajaxハンドラ *****************************************************/ var ajaxHandlers = { // コネクトできたとき responseSuccess: function(_obj){ // データの取得 (PHP:json_encodeからの戻り。evalする必要あり。) var res = eval("(" + _obj.responseText + ")"); var myInput = _obj.argument.myInput; var myContainer = _obj.argument.myContainer; var myHidden = _obj.argument.myHidden; var myForm = _obj.argument.myForm; // DataSourceインスタンスの生成 var oDS = new YAHOO.util.DataSource( res ); oDS.responseType = YAHOO.util.DataSource.TYPE_JSARRAY; oDS.responseSchema = { fields : ['name','key'] }; // AutoCompleteインスタンスの生成 var oAC = new YAHOO.widget.AutoComplete(myInput, myContainer, oDS); // そのまま読み込む oAC.resultTypeList = false; // 隠しフィールドの取得。 var myHiddenField = YAHOO.util.Dom.get(myHidden); // AutoCompleteからitemを選んだときのハンドラ var myHandler = function(sType, aArgs) { //alert(YAHOO.lang.dump(aArgs,1)); // oArgeslength=3の配列。[0]はoAC、[1]は選ばれたHTMLLIelement、[2]は選ばれたobject literal var myAC = aArgs[0]; var elLI = aArgs[1]; var oData = aArgs[2]; // hiddenのinputタグに仕込む myHiddenField.value = oData.key; }; // AutoCompleteからitemを選んだイベントに、ハンドラを仕込む oAC.itemSelectEvent.subscribe(myHandler); // submitボタンが押されたときのハンドラ var onFormSubmit = function(e, myForm) { YAHOO.util.Event.preventDefault(e); alert("商品No: " + myHiddenField.value); }; // Formのsubmitイベントに上のハンドラを仕込む。 YAHOO.util.Event.addListener(YAHOO.util.Dom.get(myForm), "submit", onFormSubmit); return { oDS: oDS, oAC: oAC }; }, // コネクトで失敗しとき responseFailure:function(_obj){ var sFailure='データの取得に失敗しました。ステータス: ' + _obj.status + '\n' + 'ステータステキスト: ' + _obj.statusText; // レスポンスオブジェクトの表示 alert(sFailure); } }; /***************************************************** * Ajax Callback *****************************************************/ var ajaxCallback = { success:ajaxHandlers.responseSuccess, failure:ajaxHandlers.responseFailure, scope: ajaxHandlers }; /***************************************************** * 初期処理 ******************************************************/ this.init = function( _config ) { submit(_config); }; return this; }; /** * MyACのインスタンス化と初期設定 * */ YAHOO.util.Event.onDOMReady( function() { var aObj = new MyAC(); var config = { submitURL : "catalogDaoGetKeyValueJSON.php", myInput : "myInput", myContainer : "myContainer", myHidden : "myHidden", myForm : "myForm" }; aObj.init(config); }, this, true ); </script> </HEAD> <body class=" yui-skin-sam"> <div id="main"> <p>AutoCompleteのサンプルです。<br/> AutoCompleteで選んだ項目に、関係する項目がSubmitされます。</p> <br> <form id="myForm" action="#"> <div id="myAutoComplete"> <input id="myInput" type="text"> <input id="mySubmit" type="submit" value="Submit"> <div id="myContainer"></div> </div> <input id="myHidden" type="hidden"> </form> </div> </body> </html>