Hatena::ブログ(Diary)

放浪するエンジニアの覚え書き このページをアンテナに追加 RSSフィード

2010-08-11

YUI2.8.1で日本語をAutoCompleteする

| 07:27 |  YUI2.8.1で日本語をAutoCompleteする - 放浪するエンジニアの覚え書き のブックマークコメント

YUIも2.8.1。以前に書いたログと同じことを日本語で実装したくて、XHRDataSourceを使って書いたのだがうまくいかない。前にログを書いていたときのYUIのバージョンは2.6だった。

仕様は簡単で、以下の初期画面で「カタカナ」で書かれた商品名を入力する。

f:id:tetsuya_odaka:20100811063250p:image

すると、以下のように(AutoCompleteだから当たり前だけど)合致する商品名が表示される。

f:id:tetsuya_odaka:20100811063251p:image

この裏では商品コードをもっていて、商品名を選択してsubmitボタンを押して商品コードが取得できていることを確認する。ページのエンコードUTF-8

f:id:tetsuya_odaka:20100811063252p:image

以前のサンプル(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でちょこちょこリクエストが出ているが、取得されているのは、上で示した文字列

うまくいかなかったコードは以下。


 
<!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を)ハンドルして「頭出し」というのもありかなぁ(仕様的には殆ど変わらないし)。


 
<!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>