それでは具体的に Ajax を使う CGI を RPG で開発した事例を紹介しよう。
下記は実行時の画面であって「次頁→」と「前頁←」ボタンでレコードの前後退を行うことが
できるという「商品マスターの照会」である。
<html> <head><title>商品マスターの照会</title> <script type="text/javascript" src="/AS400-NET.USR/JS/PROTOTYPE.JS"></script> <script type="text/javascript"><!-- var OPT = "NXT"; function Load_Data(httpObj){ document.SNDFORM.SHCODE.value = httpObj.responseXML.getElementsByTagName("SHCODE").item(0).firstChild.data; document.SNDFORM.SHNAME.value = httpObj.responseXML.getElementsByTagName("SHNAME").item(0).firstChild.data; document.SNDFORM.SHTANK.value = httpObj.responseXML.getElementsByTagName("SHTANK").item(0).firstChild.data; document.SNDFORM.SHSCOD.value = httpObj.responseXML.getElementsByTagName("SHSCOD").item(0).firstChild.data; } function INZSR(){ var CGI = "/cgi-bin/AJX001.PGM?OPT=" + OPT + "&SHCODE=" + document.SNDFORM.SHCODE.value; CGI+="&dummydate="+new Date().getTime(); new Ajax.Request(CGI, {method :"GET", onComplete:Load_Data}); } // --></script> </head> <body> <style type="text/css">body, table, td {font-size: 10pt;}</style> <body> <center> <form name="SNDFORM"> <h3>商品マスターの照会</h3> <table border="0" cellpadding="0" cellspacing="0" width="100%"> <tr height="1"><td width="180"></td><td></td></tr> <!-- // START -------------------------------------------- // --> <tr><td><font size="-1">商品コード</font></td> <td><font size="-1"><input id=SHCODE type="TEXT" name="SHCODE" maxlength="10" size="12"></font></td></tr> <tr><td><font size="-1">商品名</font></td> <td><font size="-1"><input id=SHNAME type="TEXT" name="SHNAME" maxlength="24" size="32"></font></td></tr> <tr><td><font size="-1">単価</font></td> <td><font size="-1"><input id=SHTANK type="TEXT" name="SHTANK" maxlength="8" size="10"></font></td></tr> <tr><td><font size="-1">品種コード</font></td> <td><font size="-1"><input id=SHSCOD type="TEXT" name="SHSCOD" maxlength="4" size="5"></font></td></tr> <!-- // END ---------------------------------------------- // --> <!-- // 実行ボタン // --> <tr><td colspan="2"><br> <input type="SUBMIT" value=" 実行 "> <input type="button" value=" 次頁→ " onClick="OPT='NXT';INZSR();"> <input type="button" value=" 前頁← " onClick="OPT='PRV';INZSR();"> </td></tr></table> </form> </center> <script language="JavaScript">INZSR()</script> </body> </html>
雑誌アイマガジン誌ではページの関係上、HTML の全コードを示すことができなかったが、
これがHTML の全容である。
最初に
<script type="text/javascript" src="/AS400-NET.USR/JS/PROTOTYPE.JS"></script>
によって PROTOTYPE.JS という Ajax のライブラリーを挿入していることに注意して欲しい。
次に body タグの最後に
<script language="JavaScript">INZSR()</script>
とある記述によって、この body タグが読み込まれたら
INZSR
というユーザー独自の
JavaScript 関数を起動することを指示している。
INZSR という関数は、
function INZSR(){ var CGI = "/cgi-bin/AJX001.PGM?OPT=" + OPT + "&SHCODE=" + document.SNDFORM.SHCODE.value; CGI+="&dummydate="+new Date().getTime(); new Ajax.Request(CGI, {method :"GET", onComplete:Load_Data}); }
という内容であり、
/cgi-bin/AJX001.PGM?OPT=NXT&SHCODE=NV-CF1&dummydate=20070727192030
というような 要求をサーバー(System i) に送信する。
ここで要求URL の末尾に dummydate という変数を送信しているのは、この要求がブラウザに
よってキャッシュされないようにするためである。
もしこの毎回、異なる値となるはずのdummydate =「日付 + 時刻」という変数が付加されて
いなければ最初だけは要求はサーバー(system i ) に送信されるが、送信されて得られた結果は
ブラウザへキャッシュされる。
2回目以降では、この要求に対する結果がキャッシュされているので、再びサーバー(System i)
に要求が送信されることはない。
つまり、要求は一度だけしか送信されることはない。
このように要求そのものもキャッシュされるのは具合が悪く、要求はキャッシュされることなく毎回、
必要なときには送信されなければならないのでキャッシュを防止するためにdummydate を付加
しているのである。dummydate の代わりに乱数であってもよい。
またオプションとしての変数 : OPT は NXT
=次のレコード、PRV
=前のレコード を意味する値
が入る。初めて実行される場合は
var OPT = "NXT";
として記述されているので OPT= NXT としてサーバーへ送信されることになる。
しかし、前後退ボタンによって OPT は次のように変化する。
<input type="button" value=" 次頁→ " onClick="OPT='NXT';INZSR();"> <input type="button" value=" 前頁← " onClick="OPT='PRV';INZSR();">
さて、INZSR は、GETメソッドによって上記のような要求がサーバーに送られて、サーバーからの
受信が完了すると Load_Data
という関数を実行するように指示されている。
関数 : Load_Data とは
function Load_Data(httpObj){ document.SNDFORM.SHCODE.value = httpObj.responseXML.getElementsByTagName("SHCODE").item(0).firstChild.data; document.SNDFORM.SHNAME.value = httpObj.responseXML.getElementsByTagName("SHNAME").item(0).firstChild.data; document.SNDFORM.SHTANK.value = httpObj.responseXML.getElementsByTagName("SHTANK").item(0).firstChild.data; document.SNDFORM.SHSCOD.value = httpObj.responseXML.getElementsByTagName("SHSCOD").item(0).firstChild.data; }
のような関数であって、商品コードの HTML タグでは
<input id=SHCODE type="TEXT" name="SHCODE" maxlength="10" size="12">
と記述されているので、この id は SHCODE という識別名である。
document.SNDFORM.SHCODE.value = ....
とは、SHCODE という id タグに値をセットする
ことを示している。
何の値をセットするのかと言えば、それはサーバーから取得した XML の項目の値であって
サーバーから送られてきた次のような XML :
<?xml version="1.0" encoding="Shift_JIS"?> <result> <SHCODE>NV-BS30S</SHCODE> <SHNAME>目次ビデオ</SHNAME> <SHTANK>0165000</SHTANK> <SHSCOD>0002</SHSCOD> </result>
の項目(エレメント) を SHCODE という名前によって識別して取り出して、その値を指示しているのが
httpObj.responseXML.getElementsByTagName("SHCODE").item(0).firstChild.data;
である。
これによってブラウザのメモリ上では、
<input id=SHCODE type="TEXT" name="SHCODE" maxlength="10" size="12">
は、
<input id=SHCODE type="TEXT" name="SHCODE" maxlength="10" size="12" value="NV-BS30S">
のように書き換えられることになる。残りの項目についても同様である。
それでは次に Ajax の相手をする RPG による CGI の実例を紹介する。
0001.00 H DATEDIT(*YMD/) COPYRIGHT('(C) OfficeQuattro Co,.Ltd Japan 2009-') 0002.00 F********** 商品マスター商会サンプル ***************************** 0003.00 FSHOHIN IF E K DISK EXTFILE(SHOHIN_LIB) 0004.00 F********************************************************************** 0005.00 0006.00 F*--------------------------------------------------------------------- 0007.00 F* CRTMOD=CRTRPGMOD QTEMP/AJX001 SRCFILE(PGMRLIB/QRPGLESRC) + 0008.00 F* OPTION(*NOXREF *NOSECLVL *NOEXPDDS *NOSHOWCPY) AUT(*ALL) 0009.00 F* CRTPGM=CRTPGM CGIBIN/AJX001 MODULE(QTEMP/AJX001) + 0010.00 F* BNDSRVPGM(ASNET.COM/RPGENGINE5) ACTGRP(*NEW) AUT(*ALL) 0011.00 F* USER=QTMHHTTP 0012.00 F* TYPE=RPG-CGI 0013.00 F* HTML=/AS400-NET.USR/PROJECT/AJX001/DSPDTA.HTM 0014.00 F* LIBL=CGIBIN QTRFIL QGPL QTEMP ASNET.COM 0015.00 F* CALL=HTTP://218.44.135.18/AS400-NET.USR/PROJECT/AJX001/DSPDTA.HTM 0016.00 F* TEXT= 商品マスターファイル 0017.00 F* SESSION=*NO 0018.00 F* KEEP-ALIVE=*NO 0019.00 F* SMARTCONNECTION=*NO 0020.00 F* CCSID=5026 0021.00 F* CHARSET=SHIFT_JIS 0022.00 F* 作成日 :CRTDATE=2015/05/12 時刻 11:17:10 BY QTMHHTTP 0023.00 F* 変更日 :CHGDATE=2015/05/12 時刻 11:17:10 BY QTMHHTTP 0024.00 F*--------------------------------------------------------------------- 0025.00 0026.00 D*( プログラム状況データ構造 ) 0027.00 D INFDSP SDS 0028.00 D 512A 0029.00 /COPY ASNET.USR/QRPGLESRC,INFDSP 0030.00 /COPY ASNET.USR/QRPGLESRC,PROTOTYPE6 0031.00 /COPY ASNET.USR/QRPGLESRC,ERRCONST 0032.00 D SHOHIN_LIB S 21 INZ('QTRFIL/SHOHIN') 0033.00 D XML PR 0034.00 D FIELD 10A VALUE 0035.00 D VALUE 256A VALUE 0036.00 D OPT S 3A 0037.00 D XMLBUF S 32767A INZ VARYING 0038.00 D EBC S 5I 0 INZ(1) 0039.00 D XML_VER C CONST('<?xml version="1.0" - 0040.00 D encoding="Shift_JIS" ?>') 0041.00 C*----------------------------------------------------- 0042.00 C SETKEY KLIST 0043.00 C KFLD SHCODE 0044.00 C****************************************************** 0045.00 C* メイン・ルーチン 0046.00 C****************************************************** 0047.00 C*( GET パラメータから OPT,SHCODE を取得 ) 0048.00 C EVAL OPT = CGIPARM('OPT') 0049.00 C EVAL SHCODE = CGIPARM('SHCODE') 0050.00 C*( OPT によって SHOHIN を READ または READP ) 0051.00 C SELECT 0052.00 C WHEN OPT = 'NXT' 0053.00 C SHCODE SETGT SHOHIN 0054.00 C READ SHOHIN 50 0055.00 C WHEN OPT = 'PRV' 0056.00 C SHCODE SETLL SHOHIN 0057.00 C READP SHOHIN 50 0058.00 C ENDSL 0059.00 C*( XML を作成 ) 0060.00 C EVAL XMLBUF = XML_VER 0061.00 C EVAL XMLBUF += '<RESULT>' 0062.00 C CALLP XML('SHCODE':SHCODE) 0063.00 C CALLP XML('SHNAME':SHNAME) 0064.00 C CALLP XML('SHTANK':%EDITC(SHTANK:'J')) 0065.00 C CALLP XML('SHSCOD':SHSCOD) 0066.00 C EVAL XMLBUF += '</RESULT>' + X'00' 0067.00 C*( ブラウザへ XML を送信 ) 0068.00 C MOVEL XMLBUF BUFF 512 0069.00 C CALLP SEND(%ADDR(BUFF):EBC) 0070.00 C SETON LR 0071.00 C RETURN 0072.00 *************************************************************** 0073.00 * XML プロシージャーの定義 * 0074.00 *************************************************************** 0075.00 * XML : <FIELD> ... </FIELD> を作成する 0076.00 P XML B 0077.00 D XML PI 0078.00 D FIELD 10A VALUE 0079.00 D VALUE 256A VALUE 0080.00 C EVAL XMLBUF += '<' + %TRIMR(FIELD) + '>' + 0081.00 C %TRIMR(VALUE) + '</' + %TRIMR(FIELD)+ '>' 0082.00 P XML E
このわずか 82 ステップに過ぎない RPG は、非常に小さなものであるが深い洞察を与えるに
十分な機能を含んでいる。
詳細は雑誌アイマガジン vol.3 (2007年7月27日号) を参照されることにして頂いて、
この CGI の目的は
<?xml version="1.0" encoding="Shift_JIS"?> <result> <SHCODE>NV-BS30S</SHCODE> <SHNAME>目次ビデオ</SHNAME> <SHTANK>0165000</SHTANK> <SHSCOD>0002</SHSCOD> </result>
のような XML を送信することだけである。
RPG の中には HTML に関しては、一切の記述を含んでいない。
つまり、このことがデータとフレームが完全に分離されていることを意味している。
CGI の開発者にとっては HTML や JavaScrit の知識は一切、必要でない。
このわずかな XML に関する知識があればよいだけである。
読者は今すぐこの場で 上記の Ajax のサンプルを起動して実行してみることができる。
http://218.44.135.18/AS400-NET.USR/PROJECT/AJX001/DSPDTA.HTM
をクリックすると次のような画面が表示されるはずだ。
「次頁→」と「前頁←」ボタンを押して挙動を確かめて欲しい。
HTML フレーム部分は何のチラツキもなく、データ部分だけが素早く入れ替わって更新されて
いるはずである。これこそが Ajax の動作である。
これは従来のリッチクライアント製品と同じ動作であり、しかもリッチクライアントのような再配布
の手間もなく、HTML の修正も非常にカンタンである。
おまけにリッチクライアント製品の有償購入の必要もない。
Ajax そのものが Web2.0 としての最新鋭のリッチクライアントであるのだ。
Ajax の登場によってリッチクライアント製品は、その終焉を迎えることになった。
米国では既にかなりの数のソフトウェア製品が Ajax の採用を行っており、独自のライブラリーの
配布なども行っている。
今回、紹介したサンプルはわかりやすくするために非常に簡単なものであるがAjax を使えば、
従来、不可能と思われてきた機能が次々と実現することができる。
次の項では Ajax の応用事例をご紹介しよう。