webixで表示した一覧表で直接編集し更新する動作No.016
今回は、業務アプリの中でも使用するケースがある、一覧表を表示して直接、複数行のフィールドを変更し、その行情報だけサーバに送信して更新する機能のサンプル紹介です。一覧表で直接更新する操作は、他の行を参考にしながら編集できるメリットや、同時に複数行の編集操作が可能となるメリットがあります。一見、複数行の編集を同時に実施する機能の実装は難しそうにみえますが、やり方をうまく考えて実装すれば、簡単です。今回は、更新処理だけ実装していますが、同時に行追加(新規登録)や削除も同時に実行できます。
ポイントは、編集した行にマークを付け、更新ボタン押下時に、マークのある行情報だけ抽出してサーバに送信することと、サーバに送信する情報は、配列のままでは、送信できないので、JSON形式の文字列に変換し、文字情報として送信し、サーバ側(PHP)側で、文字列をJSONフォーマットと判断して、連想配列に変更することで、配列情報をUIからサーバに簡単に送信できます。UI(Javascript)では、JSON.stringify(update_lists)のように配列情報を文字列化します。一方、サーバ側のPHPでは、json_decode($update_lists_str ,true)のようにして配列に戻します。
実際のソースを参照していただければ、イメージがつかめると思います。
webixの行編集機能には様々な操作で編集できる機能が実装されており、今回は、3つの方法をサンプルで紹介します。セルをクリックして文字列として編集する操作(editor:"text")、カレンダを使用する操作(,editor:"date")、候補値から選択する操作(editor:"select")です。詳細は、webixのマニュアルを見ていただければ、サンプルも含め記載されています。
編集操作が完了したとき(対象セルの編集が完了したタイミング)で発生するイベント(onAfterEditStop)内で、該当行は更新された状態item.editmode = 2に変更します。また、編集箇所がわかるように編集したセルの背景色も変更します。$$("table01").addCellCss(editor.row, editor.column, "green_bgcolor") 色の変更は、スタイルシートを対象セルに追加する操作で実現します。
上記の画面例は、薄緑色の背景色部分が編集操作している箇所を示しています。更新操作を実行すると、更新情報に反映され、背景色もクリアされます。
このサンプルでは、名前、所属組織、内線番号、入社日が更新できます。
更新操作が完了したら、元の情報にボタンクリックで、戻しておいてください。
サンプルのURLです・
UI側のソースです。webix_sample08.php
<?php
//https://yamasanfarm.sakuraweb.com/webix01/view/ZTEST/webix_sample08.php
// 一覧表のデータは、DBから取得
// 日付関数にmomentライブラリを使用
// phpのログ(php-error.log)は、事前に出力するようにphp.iniで記述
// 一覧表で直接更新する機能に変更
// Serch Prompt追加
$VER_INFO ="V03L01";
$TITLE_INFO = "サンプルWebix08";
$myfilename = basename(__FILE__); //自分自身のファイル名取得
define('ROOT_PATH','/home/sunsun/www/webix01'); //ソースを保存しているパス(動作環境に応じて記述する必要あり)
define('SUB_FOLDER','/webix01'); //サブフォルダを指定したURL
$userid = 'admin';
if(isset($_GET['userid'])){
$userid = $_GET['userid'];
}
$logheader = 'userid='.$userid.', '.$myfilename.':';//ログ出力時のヘッダー情報(自ファイル名,ログインIDを付与)
error_log($logheader.' start '.$myfilename);
?>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="<?php echo SUB_FOLDER; ?>/webix_GPL_1020/webix.css" type="text/css" charset="utf-8">
<script src="<?php echo SUB_FOLDER; ?>/webix_GPL_1020/webix.js"></script>
<link href="<?php echo SUB_FOLDER; ?>/webix_GPL_1020/skins/compact.css?<?php echo date('Ymd-H'); ?>" rel="stylesheet" type="text/css">
<link rel="icon" href="<?php echo SUB_FOLDER; ?>/image/webix_64.ico">
<script src="<?php echo SUB_FOLDER; ?>/commonlib/moment-with-locales.js"></script>
<title><?php echo $TITLE_INFO.' ('.$VER_INFO.')' ?></title>
<style>
.green_bgcolor {
background: #ccffcc;
}
</style>
</head>
<body>
<script type="text/javascript" charset="utf-8">
webix.i18n.setLocale("ja-JP");
var userid = '<?php echo $userid; ?>';
//本サンプルでは、組織情報をソースに記述する(実際には、この情報もサーバから取得する記述にする必要あり)
var section_options =[{ "id":"1001", "value":"総務課"},{ "id":"1002", "value":"庶務課"} ,{ "id":"1003", "value":"経理課"},{ "id":"2001", "value":"技術部"},{ "id":"3001", "value":"製造部"}];
//データを更新処理する
function data_updates_exe(update_lists){
var formData = new FormData();
formData.append("userid", userid);
formData.append("update_lists",JSON.stringify(update_lists));
var xhr =webix.ajax().sync().post("<?php echo SUB_FOLDER; ?>/rest_api/ZTEST/ZTEST_updates_userinfo.php",formData);
var resp = JSON.parse(xhr.responseText);
if(resp.resp =="ok"){
get_table_lists();
webix.message({type:"success",text:"従業員情報を更新しました。"});
return;
}
else{
webix.alert("従業員情報の更新処理でエラーしました。 code="+resp_info.error_code);
return;
}
}
//画面の初期化
webix.ui({ padding: 10,
rows:[
{view:"label", template:"<span style='font-weight:bold; font-size:180%;'>従業員一覧表<?php echo' ('.$VER_INFO.')' ?></span>"},
{ margin:5,
cols:[
{view:"text", label:"件数", labelWidth:100,name:"lists_count",id:"lists_count",value:0,width:200, labelAlign:"right",readonly:true,inputAlign:"right"},
{view:"search",id:"fl_sectionid",name:"fl_sectionid",label:"組織ID",width: 200,labelWidth: 70,inputAlign:"right",icon:"wxi-dots",readonly:true,
click:function(id,event){
$$("PT0010_fl_sectionid").setValue($$("fl_sectionid").getValue());
$$("PT0010_fl_sectionname").setValue("");
var return_lists = [{"fl_sectionid":"sectionid"},{"fl_sectionname":"sectionname"}];
$$("PT0010_return_list").setValue(JSON.stringify(return_lists));
PT0010_clear_flag = 0; //0:クリアなし 1:クリア実施
PT0010_prompt_win();
}
},
{view:"text", label:"組織名", labelWidth:80,name:"fl_sectionname",id:"fl_sectionname",width:250,labelAlign:"right",readonly:true},
{view:"button",label: "検索", id:"search_btn",name:"search_btn", width: 100
,click:function(){
get_table_lists();
}
},
{view:"button",label: "更新", id:"update_btn",name:"update_btn", width: 100
,click:function(){
var update_lists = [];
var cnt = 0;
$$("table01").eachRow(function(row){
const record = $$("table01").getItem(row);
if(record.editmode == 2){ //update
cnt += 1;
update_lists.push(record);
}
});
if(cnt == 0){
webix.alert("従業員情報の<br>更新対象の行がありません。");
return;
}
else{
webix.confirm({
title:"確認",
ok:"はい",
cancel:"いいえ",
text:cnt+"件、<br>従業員情報を更新しますか?"
})
.then(function(result){
data_updates_exe(update_lists);
})
.fail(function(){
webix.message({type:"debug",text:"更新操作をキャンセルしました。"});
});
}
}
},
{view:"button",label: "初期化", id:"init_btn",name:"init_btn", width: 100, css:"webix_danger"
,click:function(){
webix.confirm({
title:"確認",
ok:"はい",
cancel:"いいえ",
text:"テーブルを初期値に戻しますか?"
})
.then(function(result){ //はいのとき
var formData = new FormData();
formData.append("userid", userid);
var xhr =webix.ajax().sync().post("<?php echo SUB_FOLDER; ?>/rest_api/ZTEST/ZTEST_init_userinfo.php",formData);
var resp = JSON.parse(xhr.responseText);
if(resp.resp =="ok"){
get_table_lists();
webix.message({type:"success",text:"従業員情報の初期化をしました。"});
return;
}
else{
webix.alert("従業員情報の初期化でエラーしました。 code="+resp_info.error_code);
return;
}
})
.fail(function(){ //いいえの時
webix.message({type:"debug",text:"操作をキャンセルしました。"});
});
}
},
]
},
{view:"datatable", id:"table01",
columns:[
{ id:"id" ,header:"id" ,width:60 , css:{"text-align":"right"},sort:"int"},
{ id:"username" , header:["名前" , {content:"textFilter"} ] ,width:100,sort:"string", editor:"text" }, //編集は、テキスト編集
{ id:"section_id", header:["所属組織", {content:"selectFilter"}],width:100, options:section_options,sort:"string", editor:"select"}, //編集は、選択
{ id:"tel_no" , header:["内線番号", {content:"textFilter"}] ,width:100, css:{"text-align":"right"},sort:"int", editor:"text" }, //編集は、テキスト編集
{ id:"entry_date", header:["入社日" , {content:"selectFilter"}],width:120, css:{"text-align":"right"},sort:"string",format:webix.Date.dateToStr("%Y/%m/%d"),editor:"date"}, //編集は、カレンダによる編集
{ id:"edit_mode" , header:"edit_mode",width:10,hidden:true},
],
editable:true, //編集機能を有効に設定
data: [],
resizeColumn:true,
select:"cell", //クリック時にセルを選択
clipboard:true,
on:{
"onAfterEditStop" :function(state, editor, ignoreUpdate){
//編集操作が完了したときのイベントで、対象行のeditmodeを2にセットし、対象セルの背景色を変更
if(state.value != state.old){
var item = this.getItem(editor.row);
item.editmode = 2; //対象行を更新対象に設定update
$$("table01").addCellCss(editor.row, editor.column, "green_bgcolor"); //編集箇所のセルの色を変更
}
}
}
}
]
});
//画面描画時に、サーバに一覧情報を取得するAPIをコールして、情報を一覧にセット
get_table_lists();
//サーバから従業員一覧をJSONで取得する関数
function get_table_lists(){
$$("table01").clearAll();
var send_prm = {};
send_prm.userid = userid;
var fl_sectionid = $$("fl_sectionid").getValue();
if(fl_sectionid != ""){
send_prm.fl_sectionid = fl_sectionid;
}
var xhr =webix.ajax().sync().get("<?php echo SUB_FOLDER; ?>/rest_api/ZTEST/ZTEST_get_users02.php",send_prm);
var resp = xhr.responseText;
var resp_info = JSON.parse(resp);
var resp = resp_info["resp"];
if(resp_info["resp"] == "ok"){
$$("table01").parse(resp_info.val_array);
var array_count = resp_info.val_array.length;
$$("lists_count").setValue(array_count);
if(array_count > 0){
webix.message({type:"success",text:"従業員一覧を読出ししました。"});
}
else{
webix.alert("検索結果は0件です。");
}
}
else{
webix.message({type:"error",text:"従業員一覧を確認できませんでした。"});
}
}
//Search Prompt記述 通常は、別ファイルで記述して、requireすると共通化できます。
function PT0010_prompt_win(){
$$("PT0010_prompt_win").show();
PT0010_search_and_disp_exe();
}
//Serch Prompt検索処理
function PT0010_search_and_disp_exe(){
$$("PT0010_table").clearAll();
PT0010_clear_flag = 0; //0:クリアなし 1:クリア実施
var send_prm = {};
send_prm.userid = userid;
var fl_section_id = $$("PT0010_fl_sectionid").getValue();
if(fl_section_id != "") send_prm.fl_section_id =fl_section_id;
var fl_section_name = $$("PT0010_fl_sectionname").getValue();
if(fl_section_name != "") send_prm.fl_section_name =fl_section_name;
var xhr =webix.ajax().sync().get("<?php echo SUB_FOLDER; ?>/rest_api/ZTEST/ZTEST_get_sections.php",send_prm);
var resp = xhr.responseText;
var resp_info = JSON.parse(resp);
var resp = resp_info["resp"];
if(resp_info["resp"] == "ok"){
$$("PT0010_table").parse(resp_info.val_array);
var array_count = resp_info.val_array.length;
if(array_count > 0){
webix.message({type:"success",text:"組織情報を読出ししました。"});
}
else{
webix.alert("指定検索条件では、<br>検索結果は0件です。");
}
}
else{
webix.message({type:"error",text:"組織情報を確認できませんでした。"});
}
}
var PT0010_clear_flag = 0; //0:クリアなし 1:クリア実施
var PT0010_form_collection = [
{margin:5,
cols:[
{view:"text", label:"組織ID",id:"PT0010_fl_sectionid",value:"" ,labelWidth:80,width: 170,labelAlign:"right"},
{view:"text", label:"組織名",id:"PT0010_fl_sectionname",value:"" ,labelWidth:60,width: 290,labelAlign:"right"},
{view:"button", value: "検索", align:"right", width:100,id:"PT0010_search_btn",name:"PT0010_search_btn",
click:function(){
PT0010_search_and_disp_exe();
}
},
]
},
{margin:5,
cols:[
{width:360},
{view:"button", value: "クリア", align:"right", width: 100,id:"PT0010_clear_btn",name:"PT0010_clear_btn",
click:function(){
$$("PT0010_fl_sectionid").setValue("");
$$("PT0010_fl_sectionname").setValue("");
PT0010_clear_flag = 1; //0:クリアなし 1:クリア実施
$$("PT0010_table").clearAll();
}
},
{view:"button", value: "閉じる", align:"right", width: 100,
click:function(){
if( PT0010_clear_flag == 1){
//呼び出し元のデータをクリアする
var return_array = JSON.parse($$("PT0010_return_list").getValue());
for(let i=0;i< return_array.length;i++){
Object.keys(return_array[i]).forEach(function(key){
$$(key).setValue("");
});
}
}
$$('PT0010_prompt_win').hide();
}
}
]
},
{
id: "PT0010_table",
view:"datatable",
select:"row",
height:300,
columns:[
{ id:"id" ,name:"id" ,header:"行" ,width:50 ,sort:"int",css:{"text-align":"right"}},
{ id:"sectionid" ,name:"sectionid" ,header:["組織ID" ,{content:"textFilter"}],width:120,sort:"int" },
{ id:"sectionname",name:"sectionname",header:["組織名" ,{content:"textFilter"}],width:300,sort:"string"}
],
resizeColumn:true,
scroll:"xy",
datatype:"json",
data:[]
,on:{
onItemClick:function(id, e, node){
var item = this.getItem(id);
var column = id.column;
var row = id.row;
var return_array = JSON.parse($$("PT0010_return_list").getValue());
for(let i=0;i< return_array.length;i++){
Object.keys(return_array[i]).forEach(function(key){
let v1 = item[return_array[i][key]];
$$(key).setValue(v1);
});
}
$$("PT0010_prompt_win").hide();
}
}
},
{view:"text",id:"PT0010_return_list", name:"PT0010_return_list",value: "",width:400,hidden:true},
];
webix.ui({
view:"window",
id:"PT0010_prompt_win",
move:true,
head:"組織一覧",
left:100, top:100,
body:{
view:"form"
,id:"PT0010_prompt_form"
,elements:PT0010_form_collection
,width:600
}
});
</script>
</body>
</html>
サーバ側のソースです(更新処理用REST_API)ZTEST_updates_userinfo.php
<?php
//ZTEST_updates_userinfo.php
// UIからのリクエストを受信し、userテーブルを更新する
//
$FUNC_INFO = "ZTEST";
$VER_INFO ="V01L01";
include_once('env_def.php');
$myfilename = basename(__FILE__); //自分自身のファイル名取得
define('SUB_FOLDER','/webix01');
$userid = 'admin';
$logheader = 'userid='.$userid.', '.$myfilename.':';//ログ出力時のヘッダー情報(自ファイル名,ログインIDを付与)
if($_SERVER["REQUEST_METHOD"] != "POST"){
//POST以外ははじく
header("HTTP/1.0 404 Not Found");
return;
}
if(isset($_POST['userid'])){
$userid = $_POST['userid'];
}
$update_lists = array();
if(isset($_POST['update_lists'])){
$update_lists_str = $_POST['update_lists'];
$update_lists = json_decode($update_lists_str ,true);
}
$logheader = 'userid='.$userid.', '.$myfilename.':';//ログ出力時のヘッダー情報(自ファイル名,ログインIDを付与)
//共通関数の組み込み
include(ROOT_PATH.'/commonlib/svr_common_lib_v3.php');
include(ROOT_PATH.'/commonlib/Config.php');
//
//メインルーチン
//
error_log($logheader.' rest api request to '.$myfilename);
$config_obj = get_config_obj();
//データベース接続する(Mysql)
$dbh = mysql_connect($config_obj,'app01','mysql_sample'); //mysql_connect関数内でDB接続
// 静的プレースホルダを指定
$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
// エラー発生時に例外を投げる
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
//トランザクション処理を開始
$dbh->beginTransaction();
//検索するSQL文準備
$update_sql = 'UPDATE users SET username = ?,section_id = ?,tel_no = ?,entry_date = ?,updated_userid = ?,updated_on =? where id = ?';
$update_stmt = $dbh->prepare($update_sql);
for($i=0;$i<count($update_lists);$i++){
$update_stmt->bindValue(1,$update_lists[$i]["username"]);
$update_stmt->bindValue(2,$update_lists[$i]["section_id"]);
$update_stmt->bindValue(3,$update_lists[$i]["tel_no"] );
$update_stmt->bindValue(4,$update_lists[$i]["entry_date"]);
$update_stmt->bindValue(5,$userid );
$update_stmt->bindValue(6,date("Y-m-d H:i:s"));
$update_stmt->bindValue(7,$update_lists[$i]["id"]);
$update_stmt->execute();
error_log($logheader.' update users id='.$update_lists[$i]["id"]);
}
$dbh->commit();
error_log($logheader.' commit');
$dbh = null;
$resp = "ok";
$error_code = 0;
//compact関数とjson_encode関数で、JSON形式の文字列に変換
$json_data = json_encode(compact("resp","error_code"),JSON_UNESCAPED_UNICODE);
echo $json_data; //結果をecho関数で出力
?>