Datatables Editorの代替案

#背景   先日にDatatables Editorを使ってマスターデーターを変更する方法を共有しましたがDataTables Editorは無料ではありませんのでもしライセンスを買わなかったら15日後の試用期間を越えると全ての機能は無効になるはずです。自分も作者に交渉してみましたが半ヶ月ぐらいの試用期間だけを延長してもらいました。

昔に共有したEditorの使う例

ライセンスを購入しようと思ったらソロライセンスでも$119(約1万2千円)で高くて仕事がないの私は買えるはずがありません。友達にそういうことを話したらデベロッパーなので欲しいプラグインを自分で作る方がいいじゃないと言われました。そういうことで、できるだけサーバーのソースを弄らないんでjsのソースで代替案作れるようにしろうと思います。

やりたいこと: → 追加・編集・削除ボタンを押すとEditorと同じように該当のダイアローグが表示されます

#Plugins Bootstrapダイアローグはすでにご存知かも知りませんが自ら作るとすごく手間かかると思いますので今日は早く ダイアローグを動作できるように1つの無料プラグインをご紹介します。 Bootstrap3-dialogというものです。詳細は以下に貼り付けているリンクをご参考ください。

Bootstrap 3 Dialog

プロジェクトで使っている通常なプラグインは以下に列系します。

#代替案

- 実装したサーバーのソースは1ライも変更しません - Editor使用するため開発した部分を再使用する - EditorでサポートしているFieldType(select2, date)と表示フォーマットを含める

変更の前

                masterTableEditor = new $.fn.dataTable.Editor( {
                    ajax: {
                        url: '{{route("api.master.crud")}}',
                        data: function (form) {
                         form.table_name = $('#optMasterTable').select2('val');
                         return form;
                        },
                        method: 'POST',
                    },
                    table: '#master-table',
                    idSrc: "id",
                    fields: result.fields,
                } );

変更の後 : 追加・編集で使うFieldの情報を残して後でダイアローグ作成するときに使われる


var editatbleFields = result.fields;

Inline編集は後日にサポートしますが現在まだできていませんのでとりあえず以下のソースを削除する

                $('#master-table').on( 'click', 'tbody td.editable', function (e) {
                    masterTableEditor.inline( this );
                } );

削除ボタンのロジックを新しくする


//masterTableEditor
//                       .title( '{{trans("pg_system_master.dialog.delete.title")}}')
//                       .message( '{{trans("pg_system_master.dialog.delete.message")}}' )
//                        .buttons( { "label": "{{trans('pg_system_master.dialog.delete.btnOK')}}", "fn": function () { masterTableEditor.submit() } } )
//                        .remove( $(this).closest('tr') );

        //削除ダイアローグのインスタンスを作成する。タイトルやメッセージはEditor時代に定義しましたのでそのまま使う
        BootstrapDialog.show({
            title: "{{trans("pg_system_master.dialog.delete.title")}}",
            message: '{{trans("pg_system_master.dialog.delete.message")}}',
            type: BootstrapDialog.TYPE_DANGER,
            closable: true, //クローズ可能
            draggable: true,//ドラッグ可能
            spinicon: 'fa fa-spinner',//処理中を示すアイコン
            buttons: 
           	[
	            {
	                icon: 'fa fa-eraser', //ボタン
	                label:  "{{trans('pg_system_master.dialog.delete.btnOK')}}",
	                cssClass: 'btn-danger',
	                autospin: true,
	                action: function(dialogRef){
	                    dialogRef.enableButtons(false);
	                    dialogRef.setClosable(false);
	                    dialogRef.getModalBody().html('Please wait while we are performing your request!');

	                    var form = {} ;
                      	/*
                      	* アクション
                      	*/
                       	form.action   = 'remove'; 
						/*
						* Assign Data
						*/
                        var data = tblMaster.row($(this).closest('tr')).data();
						var id = data.id;
						var row = {};
						row[id] = data;
                       	form.data = row;
                        $.ajax({
                            url: '{{route("api.master.crud")}}',
                            method: 'POST',
                            data: form,
	                        success: function(data) {
	    				        dialogRef.close();
                                                //テーブルから削除する
	    				        tblMaster.row( $(this).parents('tr') ).remove().draw();
	                        },
	                        error: function(xhr) {
	                            console.log(xhr.statusText + xhr.responseText);
	                            dialogRef.close();
	                        },
                        });
	                }
	            }
	       ]
        });

新規ボタンのロジックを新しくする


$('#master-table').on( 'click', 'tbody td.editable', function (e) {
//                            masterTableEditor.inline( this );


var selectedRowItem = tblMaster.row($(this).closest('tr'));
var selectedRowItemData = selectedRowItem.data();
BootstrapDialog.show({
            title: "{{trans("pg_system_master.dialog.edit.title")}}",
            //ダイアローグの内容を編成する
            //datepickerやselect2もサポートする
            message: function(dialog){
            	var message = '';
            	message += '<div class="box-body form-horizontal">'; 
            	editatbleFields.forEach(function(item, index){
                	var itemName = item.name;
                	message += '	<div class="form-group">'; 
                	message += '		<label for="'+ itemName + '" class="col-sm-4 control-label">'+ item.label + '</label>'; 
                	message += ' 		<div class="col-sm-8">'; 
                	
                	if(item.type=='select2'){
                		message += ' <select class="form-control select2" style="width: 100%;" id ="' + itemName + '">';
                		var options = item.options;
                		options.forEach(function(opt, index){
                			message += '<option value="'+ opt.value +'" '+ ((selectedRowItemData[itemName]== opt.value)?'selected': '')  +'>'+ opt.label +'</option>';
                		});
                		message += ' </select>';
                	}else if(item.type=='date'){
                		message +=' <div class="input-group date">';
                		message +='		<div class="input-group-addon">';
                		message +='  		<i class="fa fa-calendar"></i>';
                		message +='		</div>';
                		message +=' 	<input type="text" class="form-control pull-right" id="'+itemName+'" value="'+ selectedRowItemData[itemName] +'">';
                		message +=' </div>';
            		}else{	
	                	message += '   			<input type="text" class="form-control" id="'+ itemName + '" value="'+ selectedRowItemData[itemName]+'">'; 
            		}
                	message += ' 		</div>'; 
                	message += '	</div>'; 
                });
            	message += '</div>';
            	return message;
            },
            type: BootstrapDialog.TYPE_WARNING,
            closable: true,
            draggable: false,
            spinicon: 'fa fa-spinner',
            buttons: [
				{
	                icon: 'fa fa-edit',
	                label:  "{{trans("pg_system_master.dialog.edit.btnOK")}}",
	                cssClass: 'btn-warning',
	                autospin: true,
	                action: function(dialogRef){
	                    var okButton = this;

	                    dialogRef.enableButtons(false);
	                    dialogRef.setClosable(false);
	                    var dialogBody = dialogRef.getModalBody();
                            //サーバーがわかるデーターを編成する
	                    var record = {};
	                	editatbleFields.forEach(function(item, index){
		                	var itemName = item.name;
		                	var itemValue;
		                	var element = dialogBody.find("#" + itemName);
	                    	if(item.type=='select2'){
	                    		itemValue = $('#'+item.name).select2().val();
	                    	}else if(item.type=='date'){
	                    		itemValue = $('#'+item.name).val();
	                        }else{
		                        if(item.format){
		                        	itemValue= $('#'+item.name).inputmask('unmaskedvalue');
		                        }else{
	                        		itemValue= $('#'+item.name).val();
		                        }	
	                        }                      	
	                    	record[itemName] = itemValue;
	                    	selectedRowItemData[itemName] = itemValue;
	                	});
	                    var form = {} ;
                      	/*
                      	*Action
                      	*/
                       	form.action   = 'edit'; 
						/*
						* Assign Data
						*/
						var id = selectedRowItemData.id;
						var row = {};
						row[id] = record;
                       	form.data = row;
                        $.ajax({
                            url: '{{route("api.master.crud")}}',
                            method: 'POST',
                            data: form,
	                        success: function(data) {
	                        	result = jQuery.parseJSON(data);
                                        //Validationで失敗した場合
	                        	if(result.error){
				                    var dialogBody = dialogRef.getModalBody();
				                    var errorMsg =  '<div class="alert alert-danger alert-dismissible">';
				                    errorMsg     += '	<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>';
				                    errorMsg 	 += '		<h4><i class="icon fa fa-ban"></i> System Error!</h4>';
				                    errorMsg 	 += result.error;		
				                    errorMsg 	 += '</div>';
				                    dialogBody.prepend(errorMsg);
				                    dialogRef.enableButtons(true);
				                    dialogRef.setClosable(true);
				                    okButton.stopSpin();
		                        }
                               //Exceptionの場合
                               else if(result.fieldErrors != null){
				                    var dialogBody = dialogRef.getModalBody();
				                    dialogBody.find('.form-group').removeClass('has-error');
				                    dialogBody.find('.help-block').remove();
			                        
			                        var fieldErrors = result.fieldErrors;
			                        fieldErrors.forEach(function(item, index){
				                        var element = $('#' + item.name);
				                        var formGroup = element.closest('.form-group');
				                        formGroup.addClass('has-error');
				                        var inputElementParent = formGroup.children('.col-sm-8');
				                        var statuses = item.status;
				                        statuses.forEach(function(status){
				                        	inputElementParent.append('<p class="help-block">'+ status + '</p>');
				                        });
			                        });
				                    dialogRef.enableButtons(true);
				                    dialogRef.setClosable(true);

				                    okButton.stopSpin();
                                //成功の場合
		                        }else{
		    				        dialogRef.close();
			                    	selectedRowItem.invalidate();
		    				        selectedRowItem
						    				        .data( selectedRowItemData )
						    				        .draw();
		                        }    
	                        },
	                        error: function(xhr) {
	                            console.log(xhr.statusText + xhr.responseText);
	                            dialogRef.close();
	                        },
                        });
                        
	                }
	            }
            ],
            onshown: function(dialogRef){
                //select2やdatepickerを描くため初期化は必要
                //ダイアローグが完全に表示された後に初期化する
                var body = dialogRef.getModalBody();
            	editatbleFields.forEach(function(item, index){
                	if(item.type=='select2'){
                    	$('#'+item.name).select2();
                	}else if(item.type=='date'){
                		$('#'+item.name).datepicker(item.opts);
                    }
                	if(item.format != null){
                		$('#'+item.name).inputmask(item.format);
                	}
            	});
            },
        });

} );

Editorも上に自分が提案したばかりのモジュールもわかるFieldsの定義の例:


	var editatbleFields = [ 
			{
			    label: "{{trans("pg_system_master.table.fields.name")}}",
			    name: "name",
			}, 
			{
			    label: ""{{trans("pg_system_master.table.fields.salary")}}",
			    name: "salary",
			    format:"currency",
			}, 
			{
			    label: "{{trans("pg_system_master.table.field.dob")}}",
			    name: "dob",
			    type: "date",
			    format: 'yyyy-mm-dd',
			    opts : {
			    	language: 'en',
			        format: 'yyyy-mm-dd',                  
			        pickTime: false,
			        daysOfWeekDisabled: [0,6]
			    }
			}, 
			{
			    label: "{{trans("pg_system_master.table.field.gender")}}",
			    name: "gender",
			    type:  "select2",
			    options: [
			              { label:"{{trans("pg_system_master.table.field.gender.male")}}" , value: "0" },
			              { label: {{trans("pg_system_master.table.field.gender.female")}}", value: "1" },
			   ]
			}, 	                                       
	];

追加は編集のコードとそっくり似ていますので興味を持っている読者は自分で作ってみてください

#実装結果 ①削除 スクリーンショット 2016-11-16 15.38.48.png ②新規 スクリーンショット 2016-11-16 15.39.29.png ③編集 スクリーンショット 2016-11-16 15.38.36.png