YUI2.7.0のEditable Table(編集可能な表)で編集したデータをサーバーに送信する(その1)

ここまで、YUI2.7.0にあるEditable Table(編集可能な表)について、セルの更新と妥当性検証(Validation)、行の削除を行ってきた。
変更されたデータは、Page Scope(Javascriptオブジェクト)に保存されていて、実際の利用局面では、サーバーに送って永続化するのが普通だろう。送り方としては、

  1. セルが更新されるたび、行が削除されるたびにサーバーに送る
  2. Page Scopeに保存したデータを、(なんらかのイベントで)送る

の2通りが考えられる。どちらの方法論をとるかは、利用環境やユーザビリティーに基づいて決めることになるが、ここでは、後者のコードを書いてみることにする。
サンプルは、これまで利用してきたものを拡張する。一気にサーバー送信まで書いてしまうと、前回までのログから大きくジャンプしてしまうので、今回は、

  • 更新データをPage Scopeに保管する
  • ボタンを押すことで、保管した更新データをalert表示する

の2点の拡張をすることにした。以下は、初期画面のスナップショット。Submitと表示されているボタンは、上記の後者のためのものである。

データの保管方法は以下のようにする。

  • uValueという変数を用意して、セルの更新や行の削除が発生すると、これに格納する
  • 保存は、(セル単位ではなく)行単位に行う
  • 保存の形式は、uValue[areacode] = [operation_code, [areacode=>value,state=>value,....]]とする。ここで、operation_codeは、'u'が更新、'd'が削除とする。
  • uValueは、ユニークキーであるareacodeで1つだけの要素をもつこととする。


以下は、上図について、セルの更新と削除をしたスナップショットである。2行更新して、3行目を削除した。


この状態でSubmitボタンを押すと、以下のように、uValueの値がalert画面に表示される。


以下のHTMLの全文を示す。

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> 
<html> 
<head> 
    <meta http-equiv="content-type" content="text/html; charset=utf-8"> 
<title>DataTable Validate</title> 
 
<style type="text/css"> 
</style> 

<link rel="stylesheet" type="text/css" href="../scripts/lib/yui/build/fonts/fonts-min.css" /> 
<link rel="stylesheet" type="text/css" href="../scripts/lib/yui/build/paginator/assets/skins/sam/paginator.css" /> 
<link rel="stylesheet" type="text/css" href="../scripts/lib/yui/build/datatable/assets/skins/sam/datatable.css" /> 
<script type="text/javascript" src="../scripts/lib/yui/build/yahoo-dom-event/yahoo-dom-event.js"></script> 
<script type="text/javascript" src="../scripts/lib/yui/build/animation/animation.js"></script> 
<script type="text/javascript" src="../scripts/lib/yui/build/element/element.js"></script> 
<script type="text/javascript" src="../scripts/lib/yui/build/paginator/paginator-min.js"></script> 
<script type="text/javascript" src="../scripts/lib/yui/build/datasource/datasource-min.js"></script> 
<script type="text/javascript" src="../scripts/lib/yui/build/datatable/datatable-min.js"></script> 
<!--// MyValidatorの読み込み  -->
<script type="text/javascript" src="../scripts/myznala.js">
</script> 

<style type="text/css" id="defaultstyle">
#main {
	margin: 2px;
	padding: 3px;
}

.ez_error {
	/* red(エラー用) */
	color:#ff0000;
}

.actdel img {
	/* 都道府県に付ける「×(行削除)」イメージのスタイル */
	border:0;
	margin-bottom: -3px;
}


</style>

<script type="text/javascript" src="data.js"></script> 
<script type="text/javascript">

EzTable = function() {

	var myDataSource;
	var myDataTable;

	var Dom = YAHOO.util.Dom;
	var Event = YAHOO.util.Event;

	// MyValidatorオブジェクト
	var valObj;

	// 選択された行を保管
	var wData;

	// 削除された行と行数を保管
	var delRow;
	var delNum;

	// 更新データ保管用
	var columnName;
	var uValue;

	/*
	*  都道府県に「×(行削除)」のマークを付ける
	*   (注)カラム定義より前のこと
	*/
    var ezInitFormatter = function(elCell, oRecord, oColumn, oData) { 
    	 //elCell.innerHTML= "初期値";
    	elCell.innerHTML = oData + '&nbsp' + '&nbsp' +
    				'<a class="actdel" href="#">' 
    				+ '<img class="actdel" src="../images/action_delete.png">' + '</a>';
	};
	
	/* 
	* メモ2のバリデーション
	*/
	var validateNotes2 = function(oData){

		// エラーメッセージのクリア
		clearErrorMsg();
		
		// MyValidatorをつかって数値チェックをする。
		var _ret = valObj.validate('ja','isNum',oData);
		if(_ret['isNum']!=null &&_ret['isNum'].length > 0){
			Dom.get('ez_error_res').innerHTML = _ret['isNum'];
                        return undefined;
		}

		/*
		* 以下、相関チェック
		*/
		// 数値化
		var _numData = 1 * oData;
		if("notes1" in wData){
			var _savData = 1 * wData["notes1"];
		}else{
			var _savData = 0;
		}
		
		// 相関チェック
		if(_savData >= _numData){
	            // エラーの場合、undefindを返せばいい。
		    Dom.get('ez_error_res').innerHTML = 'メモ2>メモ1でなければなりません。';
                    return undefined;
		}
		return oData;
	};

	/* 
	* クリックイベントで行データを退避する
	* 「×」マークがクリックされたら行を削除する。
	*/
	var onRowClick = function(oArgs){
		// 選ばれた行のデータ
		var _rowData = this._oAnchorRecord._oData;

		// 行データを退避
		wData = Array();
		for(var i in _rowData){
			wData[i]=_rowData[i];
		}

		// 選ばれた行の取得
		var _rowIdx = this._oAnchorRecord._nCount;
		// 行を削除する関数
		// (注) 削除するとmyDataTable.deleteRow()のインデックスがずれるので補正する。
		var _corr = 0;
		for(var i=0; i<delRow.length; i++){
			if(delRow[i]<_rowIdx) _corr++;
		}
		var _wk = _rowIdx-_corr;
		if(onClickDelete(oArgs, _wk)){
			// 削除した行の保管
			delRow[delNum++] = _rowIdx;
			// 保管
			uValue[wData['areacode']] = ['d', []];
		}
			
		
		return;
		
	};

	/*
	*  クリック時に、選択された行を削除する
	*   削除したらtrue
	*   削除しなかったらfalse
	*   を返却します。
	*/
	var onClickDelete = function(oArgs, _rowIdx){

		// イベントの発生が画面のクリックか判定する。
		var _target = Event.getTarget(oArgs.event);
		var _targetClassName = getClassName(_target);
		var _idx = _targetClassName.indexOf('actdel',0)
		if(_idx != -1){
			Event.preventDefault(oArgs.event);
			// oArgsから行を取り出して削除する。
			myDataTable.deleteRow(_rowIdx);
			return true;
		}
		return false;
	};

	/* 
	* ダブルクリックイベントでエラーメッセージを消す
	*/
	var clearErrorMsg = function(){
		Dom.get('ez_error_res').innerHTML = '';
		return;
	};

	/* 
	* 編集&(表への一時的)セーブ時にuValueへの保管を行う
	* (注)値が設定されていない列のデータはoArgsに入らない。
	*/
	var onSave = function(oArgs){
		var _data = 'レコード ';
		// 初期化
		var _u = new Array();
		for(var i in columnName){
			_u[columnName[i]] = '';
		}

		for(var key in oArgs.editor._oRecord._oData){
			_u[key] = String(oArgs.editor._oRecord._oData[key]);
		}		

		var _id = oArgs.editor._oRecord._oData['areacode'];
		uValue[_id] = ['u',_u];
		
		
		return;
	}
	
	/**
	* ボタンがクリックされたときのハンドラー
	*  uValueの内容を表示する。
	*/
	var onButtonClickHdlr = function(_evt,_obj){
		var _ret = '';
		for(var i in uValue){
			var _data = 'areacode:' + i + ' , ';
			_data += 'operation:' + uValue[i][0] + '\n';
			for(var j in uValue[i][1]){
				_data += j + ":" + uValue[i][1][j] + ' , ';
			}
			_ret += _data + '\n';
		}
		alert("変更データ: \n" + _ret);
	}
	
	/*****************************************************
	* 汎用関数
	*****************************************************/
	/**
	*  クラス名の取得
	*/
	var getClassName = function(_obj){
		if(document.all){
			//for IE
			var _keys = _obj.getAttribute('className');
		}else{
			// for FF, Chrome, Safari
			var _keys = _obj.getAttribute('class');
		}
		return _keys;
	};
	
    return {
		/**
		* 初期処理
		*/
	   	init: function() {

    		// DataTable用:列定義
        	var myColumnDefs = [
                 	{	key:"areacode",
                     	label:"コード",
                     	width:50,
                     	resizeable:true,
                     	sortable:true},
                    {	key:"state",
                        label:"都道府県",
                        width:150,
                        formatter:ezInitFormatter, // 上で定義したフォーマッターを適用する
                        resizeable:true,
                        sortable:true},
                    {	key:"notes1",
                        label:"メモ1 (編集可:数値)",
                        editor:new YAHOO.widget.TextboxCellEditor(
 			    {	
				validator:YAHOO.widget.DataTable.validateNumber,
     				defaultValue:0
    	                    }
			),
                        resizeable:true,
                        sortable:true},
                   {	key:"notes2",
                        label:"メモ 2(編集可:数値)",
                        editor:new YAHOO.widget.TextboxCellEditor(
       	               	   {	
    				validator:validateNotes2,
         			defaultValue:0
        	            }
   			),
                        resizeable:true,
                        sortable:true}
        	];

        	// DataTable用:コンフィグ属性
        	var myConfigs = {
                sortedBy:{key:"areacode",dir:"asc"},
                paginator: 
                    new YAHOO.widget.Paginator({
                    	rowsPerPage: 10,
                    	template: YAHOO.widget.Paginator.TEMPLATE_ROWS_PER_PAGE,
                    	rowsPerPageOptions: [10,25,50,100],
                    	pageLinks: 5
                	}),
               	caption:"都道府県"
        	};

        	// DataSourceのインスタンス化
	        myDataSource = new YAHOO.util.DataSource(Data.areacodes);
    	        myDataSource.responseType = YAHOO.util.DataSource.TYPE_JSARRAY;
        	myDataSource.responseSchema = {
            	fields: ["areacode","state"]
        	};

	        // DataTableのインスタンス化
    	        myDataTable = new YAHOO.widget.DataTable("table1", myColumnDefs, myDataSource, myConfigs);

		/*
		* 行の選択補助。
		*/
		// クリックでハイライトするようにハンドラを設定
	        myDataTable.subscribe("rowClickEvent",
                myDataTable.onEventSelectRow);
                myDataTable.subscribe("rowMouseoverEvent", 
            		myDataTable.onEventHighlightRow);
                myDataTable.subscribe("rowMouseoutEvent", 
            		myDataTable.onEventUnhighlightRow);

		// クリック時にデータを退避する。
                myDataTable.subscribe("rowClickEvent",
            		onRowClick);

                /*
                * セルの処理
                */
            
		// ダブルクリックでエラーメッセージを消す。
        	myDataTable.subscribe("cellDblclickEvent",
                clearErrorMsg);
		// ダブルクリックでセルのエディターを呼ぶ。
        	myDataTable.subscribe("cellDblclickEvent",
                myDataTable.onEventShowCellEditor);

        	// 編集&(表への一時的)セーブ時にuValueへの保管を行う
        	myDataTable.subscribe("editorSaveEvent", onSave);

		/*
		* MyValidatorの初期化
		*/
		valObj 	= 	new MyValidator();

		/*
		* 変数の初期化
		*/
		delRow = new Array();
		delNum = 0;

		// 列のキーを保管する
		uValue = new Object();
		columnName = new Array();
		for(var i=0; i< myColumnDefs.length; i++){
			columnName[i] = myColumnDefs[i].key;
		}

		// ボタンにハンドラーを仕掛ける。
		Event.addListener('button1', 'click', onButtonClickHdlr, Dom.get('button1'));		    
			
            
    	}, // initの終わり
        oDS: myDataSource,
        oDT: myDataTable
    };
}();

</script>
</head> 
 
<body class=" yui-skin-sam" onload="EzTable.init()">
test_datatable_validate6
<br>
<div id="main">
<p>いくつかのオプションを加えたサンプルです。<br/>
メモ1とメモ2はダブルクリックで変更できます。「メモ1<メモ2」という相関チェックを行います。<br>
都道府県名の右の<img src="../images/action_delete.png" style="margin-bottom:-3px;">をクリックすると、行を削除します。<br>
Submitボタンで、表の更新・削除の履歴を表示します。
</p>

<div id="ez_error_res" class="ez_error"></div>
<div id="table1"></div>
<form action="#">
<input id="button1" type="button" value="submit">
</form>
</div>
</body> 
</html>