ExtJSで楽しくRIA業務アプリ開発 ExJS入門25 エディタブル・グリッド(Ext.Ajaxを使った編集内容の保存)
エディタブル・グリッドはグリッド上でストアの情報が編集できるグリッドです。前回のサンプルでは編集を行ったあとのストアの情報をどこにも保存していませんでした。今回はExt.Ajaxクラスを使って、編集後のストアのデータをサーバーへ送信するサンプルを紹介します。
グリッドパネル関連の過去の記事はこちらからです。
- ExtJSで楽しくRIA業務アプリ開発 ExJS入門17 グリッド・パネル 基本編
- ExtJSで楽しくRIA業務アプリ開発 ExtJS入門18 グリッド レンダラーを使ってデザインを変える
- ExtJS入門19 グリッドにページング機能を追加する
- ExtJS入門20 グリッドにフィルタリング機能を追加する
- ExJS入門21 グリッド・セレクション・モデル
- ExJS入門22 グリッドのイベント処理(ダブルクリック、右クリック)
- ExtJS入門23 グリッドへの項目の追加と削除
- ExtJS入門24 エディタブル・グリッドの基本(グリッドパネルをエディタブル・グリッドに変更)
リファレンスはこちらから
ExtJS -3.0 日本語APIドキュメント – Ext.data.JsonStoreクラス
http://extdocs.xenophy.com/?class=Ext.data.JsonStore
ExtJS -3.0 日本語APIドキュメント – Ext.Ajaxクラス
http://extdocs.xenophy.com/?class=Ext.Ajax
PHP:
<?php
$fail = array(
'success' => false,
'errmsg' => '保存に失敗しました'
);
header("Content-Type: text/javascript; charset=utf-8");
$record = $_POST['records'];
if (!$record) {
echo json_encode($fail);
die();
};
//JSONから配列へ取り出し
$data = json_decode($record);
/**
* DB等へ保存処理
*/
$res = array(
'success' => true,
'rows' => $data,
'total' => count($data)
);
echo json_encode($res);
die();
HTML:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>ExtJS名古屋勉強会 エディタブル・グリッド 保存</title>
<!--Ext JS CSS-->
<link rel="stylesheet" href="../js/ext/resources/css/ext-all.css" type="text/css" />
<!--Ext JS-->
<script type="text/javascript" src="../js/ext/adapter/ext/ext-base.js"></script>
<script type="text/javascript" src="../js/ext/ext-all.js"></script>
<script type="text/javascript" src="editorgrid-save.js"></script>
<style TYPE="text/css">
<!--
.add {background-image: url(../img/icon/add.png) !important;}
.delete {background-image: url(../img/icon/delete.png) !important;}
.save {background-image: url(../img/icon/disk.png) !important;}
-->
</style>
</head>
<body>
<div style="width:500px;margin:0 auto;padding:15px;background-color:#D3E1F1;font-size:small;">
<p style="font-weight:bold;padding:3px;margin-bottom:10px;">
エディタブル・グリッドで変更した内容をPOSTします。
</p>
<div id="grid"></div>
<br/>
Java Script:<a href="editorgrid-save.js">editorgrid-save.js</a><br/>
</div>
</body>
</html>
Java Script:
//グリッドへの追加削除
Ext.onReady(function()
{
//保存リクエスト
var onSave = function(){
//編集をとめる
grid.stopEditing();
var modifiedRecords = grid.getStore().getModifiedRecords();
if (modifiedRecords.length > 0) {
//POSTするパラメータ
var params = [];
//編集があったレコードからJSONでデータを取り出す。
Ext.each(modifiedRecords,function(record){
params.push(record.data);
});
//JSONをエンコードする。
params = Ext.encode(params);
//Ajaxリクエスト処理
//ローディングマスクを設置
Ext.getBody().mask('保存中です。');
//Ext.Ajaxクラスを使用してリクエスト
Ext.Ajax.request({
url:'save.php', //ポストするURL
params:{
records:params
},
success:function(res,opt){ //成功時
var res = Ext.decode(res.responseText);
if (res.success){
//正しく保存できた場合
//変更をストアにセットする。(赤い三角が消える)
grid.getStore().commitChanges();
//grid.getStore().reload();
}else{
Ext.Msg.alert('エラー','保存に失敗しました。');
}
//ローディングマスクを外す
Ext.getBody().unmask();
},
failure:function(res,opt){ //接続エラーなど
Ext.Msg.alert('エラー','保存に失敗しました。');
//ローディングマスクを外す
Ext.getBody().unmask();
}
});
}
}
var grid = new Ext.grid.EditorGridPanel({
renderTo:'grid',
title:'エディタブル・グリッドの保存',
height:210,
width:400,
//カラムモデル
cm:new Ext.grid.ColumnModel([
{
id:'title',
header:'項目名',
dataIndex:'title',
width:100,
editor:{ //カラムのエディタを指定
xtype:'textfield',
emptyText:'入力してください。'
},
renderer:function(v){ //カラムのデータが空のとき表示
return v ? v : 'ダブルクリックで編集';
}
}
]),
//セレクションモデル
sm:new Ext.grid.RowSelectionModel({
singleSelect:true
}),
//ストア
store:new Ext.data.JsonStore({
fields:['title'],
autoSave:false
}),
//GridViewのコンフィグを設定
viewConfig:{
deferEmptyText:false,
emptyText:'グリッド項目はありません',
forceFit:true
},
tbar:[
{
text:'追加',
iconCls:'add',
handler:function(btn){ //グリッドに項目を追加する処理
//編集中の場合は編集をやめる
grid.stopEditing();
var data = {};
var RecordType = grid.getStore().recordType;
var newItem = new RecordType(data);
grid.getStore().insert(0,newItem);
}
},
{
text:'削除',
iconCls:'delete',
handler:function(btn){ //選択したレコードを削除する処理
//編集中の場合は編集をやめる
grid.stopEditing();
//1.選択されている項目を取得
var record = grid.selModel.getSelected();
if (record) {
//2.recordをremoveでストアから取り除く
grid.getStore().remove(record);
}
}
},'->',
{
text:'保存',
iconCls:'save',
handler:function(){
onSave();
}
}
]
});
});
実行結果:
エディタブル・グリッドではストアの内容を保存するタイミングはいくつかのパターンがあります。一つは対象のレコードの編集が終わったあとに、すぐリクエストをかける方法。この方法では、特に保存ボタンをつけずに、EditorGridクラスのaftereditイベントの中で保存を実行します。またストアのwriterを設定して、変更を保存することもできます。
今回は明確に保存ボタンを作成して、グリッドの編集を終えて保存ボタンを押したときに、変更のあったレコードを送信するようにします。保存ボタンはtbarに設置して、ボタンが押されるとonSave()関数が実行されるようになっています。保存処理は全てこの関数の中で記述しています。
onSave()の大まかな処理の流れは、
編集をやめる → 変更のあったレコードの取り出し → レコードからJSONデータの取り出し → ローディングマスクの設置 → AJAXリクエスト → リクエスト結果のハンドリング → ストアを更新 → ローディングマスクの除去。
となっています。
まず編集をとめます。tbarに保存ボタンがあるので、編集中でもボタンが押せてしまします。grid.storeEditing()で編集中のエディタを止めます。
//編集をとめる
grid.stopEditing();
次に編集されたデータの取出しです。これはストアのgetModifiedRecords()メソッドを使うと一発で取得できます。getModifiedRecords()はストアが最後にコミットされてから変更があったレコード記憶し配列で返します。ここで帰ってくるレコードはオブジェクトです。サーバーへはJSONで送信したいので全てのレコードからデータをJSONで取り出します。Ext.each()はphpのforeachのように第一引数に設定された配列を列挙して、第二引数に設定された関数を実行していきます。ここではparams配列に各レコードのJSONデータを取り出して追加しています。
//ストアから保存する対象のレコードを全て取得
var modifiedRecords = grid.getStore().getModifiedRecords();
//POSTするパラメータを初期化
var params = [];
//編集があったレコードからJSONでデータを取り出す。
Ext.each(modifiedRecords,function(record){
params.push(record.data);
});
次は取り出したJSONを文字列にエンコードします。これはJSONの配列をそのままポストでできないためです。またリクエスト中にローディングマスクを設置します。マスクの設定は簡単で対象のExtエレメントのmaskメソッドを呼ぶだけです。引数にマスクに表示したい文字列を設定します。
//JSONをエンコードする。
params = Ext.encode(params);
//Ajaxリクエスト処理
//ローディングマスクを設置
Ext.getBody().mask('保存中です。');
さていよいよAjaxリクエストですが、ExtJSではAjaxリクエストの際にはExt.Ajaxクラスを使用します。Ext.Ajaxはシングルトンです。Ext.Ajax.request()としてリクエストを実行します。引数にリクエストの設定をオブジェクトで指定します。
Ext.Ajax.requestの引数
url:リクエスト先のURL
params:リクエストの際に送信するパラメータ
success:リクエスト成功時のコールバック関数
failure:リクエスト失敗時のコールバック関数
今回はsave.phpへ保存します。paramsには先ほど取り出してエンコードしたJSONデータを設定します。リクエストが成功した場合はsuccessに設定したコールバック関数が実行されます。failureは失敗したに呼ばれます。どちらも同じ引数(response:レスポンスデータを含むオブジェクト,option:リクエストしたパラメータ)を持って実行されます。
//Ext.Ajaxクラスを使用してリクエスト
Ext.Ajax.request({
url:'save.php', //ポストするURL
params:{
records:params
},
success:function(res,opt){
//成功時
},
failure:function(res,opt){
//接続エラーなど
}
});
save.phpはサーバーの処理の実行結果をJSON形式で返します。今回のサンプルでは保存が成功した場合は
{
success:true,
total:10,
row:{/*変更した配列*/}
}
失敗した場合は
{
success:false,
errmsg:'エラーメッセージ'
}
がレスポンスとして返されます。
successはサーバへの接続が成功した場合に呼ばれます。そのため何かのエラーで保存が失敗しレスポンスにfalseが返ってきた場合もsuccess側のコールバックが呼ばれます。本当に保存が成功しているかどうかはレスポンスの中のsuccessを見て判断します。
successの第一引数はレスポンスデータを含むXMLHttpRequestオブジェクトです。この中のresponseTextに実際のレスポンスがあります。今回の場合このレスポンスは文字列にエンコードされたJSONです。そのためresponseTextを取り出してJSONにエンコードする必要があります。
success:function(res,opt){ //成功時
var res = Ext.decode(res.responseText);
if (res.success){
//正しく保存できた場合
//変更をストアにセットする。(赤い三角が消える)
grid.getStore().commitChanges();
//grid.getStore().reload();
}else{
Ext.Msg.alert('エラー','保存に失敗しました。');
}
//ローディングマスクを外す
Ext.getBody().unmask();
},
failure:function(res,opt){ //接続エラーなど
Ext.Msg.alert('エラー','保存に失敗しました。');
//ローディングマスクを外す
Ext.getBody().unmask();
}
});
ここでは変数resにレスポンスからJSONデータを取り出します。
res.successがtrueの場合のみ、保存が成功しているのでストアの後処理を実行します。今回は単純に変更があったものをコミットして、modifiedRecordsをクリアしているだけですが、実際にはサーバー上との整合性のために新規追加項目のID等を振りなおす必要があります。もしくはストアをリロードしてサーバーの最新情報とグリッドを同期します。
最後にマスクを外します。マスクを追加したときと同じように対象のExtエレメントのunmaskメソッドを実行すれば外せます。
今回紹介したサンプルではサーバーサイドで実際にDB等に保存は行っていません。実際にAjaxのポストを取得してDBなどに保存するようにサーバーサイドのサンプルコードを変更して試してください。ExtJSでの基本的なAjaxのやりとりはJSONがよいと思います。phpであれば特に面倒な処理もなくjson_encodeとjson_decodeを使えば変換も楽におこなえます。
AjaxのやりとりはFirefoxのアドオンFirebugを使うと分かりやすいです。
Ajaxリクエスト自体はグリッドだけでなく、どこでも使えるので、色々なリクエストを試してみるとよいと思います。






[...] ExJS入門25 エディタブル・グリッド(Ext.Ajaxを使った編集内容の保存) [...]
ExJS入門26 エディタブル・グリッド(色々なエディターを設定する) | ExtJSで楽しくRIA業務アプリ開発
27 4 月 10 at 12:43 PM
[...] ExJS入門25 エディタブル・グリッド(Ext.Ajaxを使った編集内容の保存) [...]
ExJS入門29 JSONPを使ってYouTube Data APIをグリッドパネルに読み込む | ExtJSで楽しくRIA業務アプリ開発
6 5 月 10 at 1:45 PM
[...] ExJS入門25 エディタブル・グリッド(Ext.Ajaxを使った編集内容の保存) [...]
ExJS入門30 Twitter Search APIを使ったグリッドパネル | ExtJSで楽しくRIA業務アプリ開発
7 5 月 10 at 11:51 AM