【JQuery】Ajax で動的に登録・更新・削除した後に DataTables にデータを再表示する
おはようございます。
昨日に引き続き、DataTables を利用したページの作成をしていきます。
今回はデータを登録・更新・削除した後、データを再取得して DataTables をリロードする方法を試してみます。
プログラムは前回のものを流用します。
【JQuery】DataTables に Ajax で動的に取得したデータを表示する
スポンサーリンク
画面の修正
主な変更点
1.CSSを外出し
2.検索用コントロールのスタイル変更
3.登録、更新用のモーダルダイアログ(Bootstrap)を追加
static/css/style.css
.container {
border: 1px solid black;
padding: 1rem;
margin: 1rem;
}
.form-inline .label {
margin-top: 10px;
}
input {
ime-mode: active;
}
input.ime-disabled {
ime-mode: disabled;
}
input.ime-inactive {
ime-mode: inactive
}
.table > tbody > tr.active > td,
.table > tbody > tr.active > th {
background-color: #e1f2fe;
}
templates/index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>DataTableサンプル</title>
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.19/css/dataTables.bootstrap.min.css"/>
<link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"/>
<link rel="stylesheet" href="{{ static_url('css/style.css') }}"/>
</head>
<body>
<div class="container container-fluid">
<div>
名前:
<input type="text" class="input-sm ime-disabled" id="searchName" placeholder="名前" required>
<button id="searchButton" type="button" class="btn btn-sm btn-info">検索</button>
</div>
<div>
<table id="myTable" class="table table-striped tagle-bordered">
<thead>
<tr>
<th>No</th>
<th>名前</th>
<th>性別</th>
<th>年齢</th>
<th>種別</th>
<th>好物</th>
</tr>
</thead>
<tbody>
</tbody>
<tfoot>
<tr>
<th>No</th>
<th>名前</th>
<th>性別</th>
<th>年齢</th>
<th>種別</th>
<th>好物</th>
</tr>
</tfoot>
</table>
</div>
<div>
<hr>
<div class="pull-left">
<button id="registButton" type="button" class="btn btn-primary">登録</button>
<button id="updateButton" type="button" class="btn btn-success" disabled>更新</button>
</div>
<div class="pull-right">
<button id="deleteButton" type="button" class="btn btn-danger" disabled>削除</button>
</div>
</div>
</div>
<div id="form" class="modal fade" tabindex="-1">
<div class="modal-dialog modal-dialog-centered ">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4 id="dialogTitle" class="modal-title">登録</h4>
</div>
<div class="modal-body">
<div class="container-fluid">
<div class="row">
<div class="col-xs-12">
<form class="form-horizontal">
<div class="form-group">
<div class="form-inline">
<div class="col-sm-4">
<label class="control-label">No</label>
<span class="label label-danger pull-right">必須</span>
</div>
<div class="input-group col-sm-2">
<input type="text" class="form-control ime-disabled" id="inputNo" placeholder="No" required>
</div>
</div>
</div>
<div class="form-group">
<div class="form-inline">
<div class="col-sm-4">
<label class="control-label">名前</label>
<span class="label label-danger pull-right">必須</span>
</div>
<div class="input-group col-sm-2">
<input type="text" class="form-control" id="inputName" placeholder="名前" required>
</div>
</div>
</div>
<div class="form-group">
<div class="form-inline">
<div class="col-sm-4">
<label class="control-label">性別</label>
<span class="label label-danger pull-right">必須</span>
</div>
<div class="input-group col-sm-2">
<input type="text" class="form-control" id="inputSex" placeholder="性別">
</div>
</div>
</div>
<div class="form-group">
<div class="form-inline">
<div class="col-sm-4">
<label class="control-label">年齢</label>
<span class="label label-danger pull-right">必須</span>
</div>
<div class="input-group col-sm-2">
<input type="text" class="form-control ime-disabled" id="inputAge" placeholder="年齢">
</div>
</div>
</div>
<div class="form-group">
<div class="form-inline">
<div class="col-sm-4">
<label class="control-label">種別</label>
<span class="label label-danger pull-right">必須</span>
</div>
<div class="input-group col-sm-7">
<select id="inputKind" class="form-control" >
<option value="01">キジトラ</option>
<option value="02">長毛種(不明)</option>
<option value="03">ミケ(っぽい)</option>
<option value="04">サビ</option>
<option value="09">その他</option>
</select>
</div>
</div>
</div>
<div class="form-group">
<div class="form-inline">
<div class="col-sm-4">
<label class="control-label">好物</label>
<span class="label label-success pull-right">任意</span>
</div>
<div class="input-group col-sm-7">
<input type="text" class="form-control" id="inputFavorite" placeholder="好物">
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<div id="inputError" class="pull-left" style="color:red; padding:5px;"></div>
<button id="sendRegistButton" type="button" class="btn btn-primary"><i class="fa fa-check"></i> 登録</button>
<button id="sendUpdateButton" type="button" class="btn btn-primary"><i class="fa fa-check"></i> 修正</button>
<button type="button" class="btn btn-default" data-dismiss="modal"><i class="fa fa-remove"></i> 閉じる</button>
</div>
</div>
</div>
</div>
</body>
<script type="text/javascript" src="https://code.jquery.com/jquery-3.3.1.js"></script>
<script type="text/javascript" src="https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
<script type="text/javascript" src="https://cdn.datatables.net/1.10.19/js/jquery.dataTables.min.js"></script>
<script type="text/javascript" src="https://cdn.datatables.net/1.10.19/js/dataTables.bootstrap.min.js"></script>
<script type="text/javascript" src="{{ static_url('js/script.js') }}"></script>
</html>プログラムの修正
クライアント
主な変更点
1.テーブル行クリック時に行のスタイル(クラス)を変更する
2.各種ボタン(登録・更新・削除)クリック時の処理を追加
3.ダイアログの各種ボタン(登録・更新)クリック時の処理(Ajax)を追加
static/js/script.js
$(document).ready( function () {
$('#myTable').DataTable({
'paging' : true,
'pageLength' : 5,
'lengthChange': false,
'searching' : true,
'ordering' : true,
'info' : true,
'autoWidth' : true,
"scrollCollapse": true,
'scrollX' : true,
'scrollY' : '185px',
'tabIndex' : -1,
'order' : [[ 0, 'asc' ]],
'colReorder' : true,
'serverSide' : false,
'ajax' : {
'url' : '/init',
'type' : 'POST',
'data' : function ( d ) {
d.searchName = $("#searchName").val();
}
},
'columns' : [
{ 'data' : 'no', width: 40 },
{ 'data' : 'name', width: 60 },
{ 'data' : 'sex', width: 40 },
{ 'data' : 'age', width: 40 },
{ 'data' : 'kind_name', width: 100 },
{ 'data' : 'favorite', width: 120 },
],
'language' : {
'decimal': ".",
'emptyTable': "表示するデータがありません。",
'info': "_START_ ~ _END_ / _TOTAL_ 件中",
'infoEmpty': "0 ~ 0 / 0 件",
'infoFiltered': "(合計 _MAX_ 件からフィルタリングしています)",
'infoPostFix': "",
'thousands': ",",
'lengthMenu': "1ページ _MENU_ 件を表示する",
'loadingRecords': "読み込み中...",
'processing': "処理中...",
'search': "絞り込み:",
'zeroRecords': "一致するデータが見つかりません。",
'paginate': {
'first': "最初",
'last': "最後",
'next': "次",
'previous': "前"
}
}
});
// テーブル行クリックの設定
$('#myTable tbody').on("click", "tr", function() {
if ($(this).find('.dataTables_empty').length == 0) {
var owner = $(this);
$("#myTable tr").removeClass("active");
owner.addClass("active");
$("#updateButton").prop("disabled", false);
$("#deleteButton").prop("disabled", false);
}
});
// 検索ボタンクリック時の処理
$("#searchButton").on("click", function() {
$('#myTable').DataTable().ajax.url("/search").load();
$('#myTable').DataTable().ajax.reload();
});
//
$("#registButton").on("click", function() {
// ダイアログ表示
$('#form').on('show.bs.modal', function (event) {
// コントロール制御
$("#form #dialogTitle").text("新規登録");
$("#form #sendRegistButton").show();
$("#form #sendUpdateButton").hide();
$("#form #inputNo").prop("disabled", true);
// フォーカス
setTimeout(function(){
$("#inputNo").focus();
}, 500);
}).modal("show");
});
$("#sendRegistButton").on("click", function(){
var param = {
no : $("#inputNo").val()
, name : $("#inputName").val()
, sex : $("#inputSex").val()
, age : $("#inputAge").val()
, kind_cd : $("#inputKind").val()
, favorite : $("#inputFavorite").val()
}
$.ajax({
url: "http://localhost:8080/regist",
type: "POST",
data: JSON.stringify(param),
success: function(jsonResponse) {
jsonResponse = jsonResponse.replace( /\\/g , "" );
var data = JSON.parse(jsonResponse);
// テーブル更新
$('#myTable').DataTable().ajax.url("/search").load();
$('#myTable').DataTable().ajax.reload();
// フォームを閉じる
$("#form").modal("hide");
},
error: function() {
}
});
});
$("#updateButton").on("click", function() {
var selectedRows = $('#myTable').DataTable().rows('.active').data();
var param = {
no : selectedRows[0].no
}
$.ajax({
url: "http://localhost:8080/getRecord",
type: "POST",
data: JSON.stringify(param),
success: function(jsonResponse) {
var data = JSON.parse(jsonResponse);
var cat = data[0];
// ダイアログ表示
$('#form').on('show.bs.modal', function (event) {
// 取得したデータのセット
$("#inputNo").val(cat.no);
$("#inputName").val(cat.name);
$("#inputSex").val(cat.sex);
$("#inputAge").val(cat.age);
$("#inputKind").val(cat.kind_cd);
$("#inputFavorite").val(cat.favorite);
// コントロール制御
$("#form #dialogTitle").text("更新");
$("#form #sendRegistButton").hide();
$("#form #sendUpdateButton").show();
$("#form #inputNo").prop("disabled", true);
setTimeout(function(){
$("#inputName").focus();
}, 500);
}).modal("show");
},
error: function() {
}
});
});
$("#sendUpdateButton").on("click", function(){
var param = {
no : $("#inputNo").val()
, name : $("#inputName").val()
, sex : $("#inputSex").val()
, age : $("#inputAge").val()
, kind_cd : $("#inputKind").val()
, favorite : $("#inputFavorite").val()
}
$.ajax({
url: "http://localhost:8080/update",
type: "POST",
data: JSON.stringify(param),
success: function(jsonResponse) {
jsonResponse = jsonResponse.replace( /\\/g , "" );
var data = JSON.parse(jsonResponse);
// テーブル更新
$('#myTable').DataTable().ajax.url("/search").load();
$('#myTable').DataTable().ajax.reload();
// フォームを閉じる
$("#form").modal("hide");
},
error: function() {
}
});
});
$("#deleteButton").on("click", function(){
var selectedRows = $('#myTable').DataTable().rows('.active').data();
var param = {
no : selectedRows[0].no
}
$.ajax({
url: "http://localhost:8080/delete",
type: "POST",
data: JSON.stringify(param),
success: function(jsonResponse) {
// テーブル更新
$('#myTable').DataTable().ajax.url("/search").load();
$('#myTable').DataTable().ajax.reload();
},
error: function() {
}
});
});
});サーバー
主な変更点
1.MySQLUtilクラスを追加してデータ操作の処理をまとめる
2.登録・更新・削除に対応するメソッドの追加
MySQLUtil.py
import mysql.connector
import logging
from contextlib import closing
class MySQLUtil:
"""
MySQL 操作用クラス
"""
def __init__(self, host="localhost", port="3306", user="USER01", password="USER01", database="DB01"):
self.config = {
"host": host,
"port": port,
"user": user,
"password": password,
"database": database
}
def insert_data(self, data):
"""
データを登録します
:param data:
:return:
"""
with closing(mysql.connector.connect(**self.config)) as conn:
c = conn.cursor()
# データ登録
sql = "INSERT INTO TBLCAT VALUES (%s,%s,%s,%s,%s,%s)"
c.execute(sql, data)
c.close()
conn.commit()
def update_data(self, data):
"""
データを更新します
:param data:
:return:
"""
with closing(mysql.connector.connect(**self.config)) as conn:
c = conn.cursor()
# データ登録
sql = "UPDATE TBLCAT SET NAME = %s, " \
" SEX = %s, " \
" AGE = %s, " \
" KIND_CD = %s, " \
" FAVORITE = %s " \
"WHERE " \
" NO = %s"
c.execute(sql, data)
c.close()
conn.commit()
def delete_data(self, no):
"""
データを削除します
:return:
"""
logging.info("delete_data")
with closing(mysql.connector.connect(**self.config)) as conn:
c = conn.cursor()
# データクリア
sql = "DELETE FROM TBLCAT WHERE NO = '" + no + "'"
c.execute(sql)
c.close()
conn.commit()
def get_data(self, no="", search_name=""):
"""
データを取得します
:return:
"""
result = []
with closing(mysql.connector.connect(**self.config)) as conn:
c = conn.cursor(dictionary=True)
# SQL組み立て
sql = "SELECT C.NO, C.NAME, C.SEX, C.AGE, C.KIND_CD, K.KIND_NAME, C.FAVORITE FROM TBLCAT C"
sql += " LEFT OUTER JOIN MSTKIND K ON ( C.KIND_CD = K.KIND_CD)"
if no != "":
sql += " WHERE NO = '" + no + "'"
else:
sql += " WHERE C.NAME LIKE '" + search_name + "%'"
sql += " ORDER BY NO"
c.execute(sql)
for r in c.fetchall():
result.append({
"no": r['NO'],
"name": r['NAME'],
"sex": r['SEX'],
"age": r['AGE'],
"kind_cd": r['KIND_CD'],
"kind_name": r['KIND_NAME'],
"favorite": r['FAVORITE'],
})
return result
def get_next_no(self):
"""
ユーザー毎にカレンダーIDの最大値+1を返します
:return:
"""
result = 0
with closing(mysql.connector.connect(**self.config)) as conn:
c = conn.cursor(dictionary=True)
sql = "SELECT MAX(NO) + 1 AS NO FROM TBLCAT"
c.execute(sql)
result = c.fetchone()
return result[r"NO"]
Main.py
import json
import logging
import os
import tornado.ioloop
import mysql.connector
from tornado.web import RequestHandler
from tornado.options import options
from contextlib import closing
from Utils.MySQLUtil import MySQLUtil
class MainHandler(RequestHandler):
"""
画面表示
"""
def get(self):
logging.info("MainHandler [get]")
self.render("index.html")
class InitHandler(RequestHandler):
"""
一覧初期化用
"""
def post(self):
list = []
result = {
'data': list
}
self.write(json.dumps(result, ensure_ascii=False))
class SearchHandler(RequestHandler):
"""
データ検索
"""
def initialize(self):
logging.info("SearchHandler [initialize]")
def post(self):
"""
データをJSON形式で返します
:return:
"""
logging.info("SearchHandler [post]")
search_name = self.get_argument("searchName")
mysql = MySQLUtil()
data_list = mysql.get_data(search_name=search_name)
result = {
'data': data_list
}
self.write(json.dumps(result, ensure_ascii=False))
class GetRecordHandler(RequestHandler):
"""
プライマリキーを指定してデータ取得
"""
def initialize(self):
logging.info("GetRecordHandler [initialize]")
def post(self):
logging.info("GetRecordHandler [post]")
param = json.loads(self.request.body)
no = str(param["no"])
mysql = MySQLUtil()
result = mysql.get_data(no=no)
self.write(json.dumps(result, ensure_ascii=False))
class RegistHandler(RequestHandler):
"""
データ登録
"""
def initialize(self):
logging.info("RegistHandler [initialize]")
def post(self):
logging.info("RegistHandler [post]")
mysql = MySQLUtil()
param = json.loads(self.request.body)
no = mysql.get_next_no()
data = [
no,
param["name"],
param["sex"],
param["age"],
param["kind_cd"],
param["favorite"],
]
mysql.insert_data(data)
result = {
'result': "success"
}
self.write(json.dumps(result, ensure_ascii=False))
class UpdateHandler(RequestHandler):
"""
更新
"""
def initialize(self):
logging.info("UpdateHandler [initialize]")
def post(self):
logging.info("UpdateHandler [post]")
mysql = MySQLUtil()
param = json.loads(self.request.body)
data = [
param["name"],
param["sex"],
param["age"],
param["kind_cd"],
param["favorite"],
param["no"],
]
mysql.update_data(data)
result = {
'result': "success"
}
self.write(json.dumps(result, ensure_ascii=False))
class DeleteHandler(RequestHandler):
"""
削除
"""
def initialize(self):
logging.info("RegistHandler [initialize]")
def post(self):
logging.info("RegistHandler [post]")
mysql = MySQLUtil()
param = json.loads(self.request.body)
no = str(param["no"])
mysql.delete_data(no)
result = {
'result': "success"
}
self.write(json.dumps(result, ensure_ascii=False))
app = tornado.web.Application([
(r"/", MainHandler),
(r"/init", InitHandler),
(r"/search", SearchHandler),
(r"/getRecord", GetRecordHandler),
(r"/regist", RegistHandler),
(r"/update", UpdateHandler),
(r"/delete", DeleteHandler),
],
template_path=os.path.join(os.getcwd(), "templates"),
static_path=os.path.join(os.getcwd(), "static"),
)
if __name__ == "__main__":
options.parse_command_line()
app.listen(8080)
logging.info("server started")
tornado.ioloop.IOLoop.instance().start()
操作してみる
検索
登録
親画面の「登録」ボタンをクリックで表示。
内容を入力してダイアログの「登録」ボタンをクリック。
No5にデータが追加されました。ちゃんと件数表示も更新されています。
更新
親画面で No5 を選択後「更新」ボタンをクリックで表示。内容は一度DBに問い合わせてフォームにセットしています。
種別を変更して「修正」ボタンをクリック。
一覧の種別も変更されました。
削除
No5 を選択して「削除」ボタンをクリック。
No5 が一覧から削除されました。
まとめ
ちょっと長くなってしまいましたが、一通り、DataTables と サーバーとの通信はできましたね。
ここまで出来れば、ちょっとしたシステムにも組み込むことができそうです。
DataTablesはまだまだ色々出来そうなので、また時間が出来たら試してみたいと思います。
ではでは。
ディスカッション
コメント一覧
まだ、コメントがありません